mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
6f7d0cb1c3
This makes breaking changes to add underscores to many non-standard function names provided by the c library. MODE=tiny is now tinier and we now use smaller locks that are better for tiny apps in this mode. Some headers have been renamed to be in the same folder as the build package, so it'll be easier to know which build dependency is needed. Certain old misguided interfaces have been removed. Intel intrinsics headers are now listed in libc/isystem (but not in the amalgamation) to help further improve open source compatibility. Header complexity has also been reduced. Lastly, more shell scripts are now available.
4108 lines
141 KiB
C
4108 lines
141 KiB
C
/*
|
|
* QuickJS Javascript Engine
|
|
*
|
|
* Copyright (c) 2017-2021 Fabrice Bellard
|
|
* Copyright (c) 2017-2021 Charlie Gordon
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
#include "libc/assert.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/mem/gc.internal.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "third_party/quickjs/internal.h"
|
|
|
|
asm(".ident\t\"\\n\\n\
|
|
QuickJS (MIT License)\\n\
|
|
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
|
Copyright (c) 2017-2021 Charlie Gordon\"");
|
|
asm(".include \"libc/disclaimer.inc\"");
|
|
/* clang-format off */
|
|
|
|
static const uint16_t func_kind_to_class_id[] = {
|
|
[JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION,
|
|
[JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION,
|
|
[JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION,
|
|
[JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION,
|
|
};
|
|
|
|
static JSValue JS_CallConstructorInternal(JSContext *,
|
|
JSValueConst,
|
|
JSValueConst,
|
|
int, JSValue *, int);
|
|
|
|
JSValue js_closure2(JSContext *ctx, JSValue func_obj,
|
|
JSFunctionBytecode *b,
|
|
JSVarRef **cur_var_refs,
|
|
JSStackFrame *sf)
|
|
{
|
|
JSObject *p;
|
|
JSVarRef **var_refs;
|
|
int i;
|
|
p = JS_VALUE_GET_OBJ(func_obj);
|
|
p->u.func.function_bytecode = b;
|
|
p->u.func.home_object = NULL;
|
|
p->u.func.var_refs = NULL;
|
|
if (b->closure_var_count) {
|
|
var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
|
|
if (!var_refs)
|
|
goto fail;
|
|
p->u.func.var_refs = var_refs;
|
|
for(i = 0; i < b->closure_var_count; i++) {
|
|
JSClosureVar *cv = &b->closure_var[i];
|
|
JSVarRef *var_ref;
|
|
if (cv->is_local) {
|
|
/* reuse the existing variable reference if it already exists */
|
|
var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg);
|
|
if (!var_ref)
|
|
goto fail;
|
|
} else {
|
|
var_ref = cur_var_refs[cv->var_idx];
|
|
var_ref->header.ref_count++;
|
|
}
|
|
var_refs[i] = var_ref;
|
|
}
|
|
}
|
|
return func_obj;
|
|
fail:
|
|
/* bfunc is freed when func_obj is freed */
|
|
JS_FreeValue(ctx, func_obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
JSValue js_closure(JSContext *ctx, JSValue bfunc,
|
|
JSVarRef **cur_var_refs,
|
|
JSStackFrame *sf)
|
|
{
|
|
JSFunctionBytecode *b;
|
|
JSValue func_obj;
|
|
JSAtom name_atom;
|
|
b = JS_VALUE_GET_PTR(bfunc);
|
|
func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]);
|
|
if (JS_IsException(func_obj)) {
|
|
JS_FreeValue(ctx, bfunc);
|
|
return JS_EXCEPTION;
|
|
}
|
|
func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf);
|
|
if (JS_IsException(func_obj)) {
|
|
/* bfunc has been freed */
|
|
goto fail;
|
|
}
|
|
name_atom = b->func_name;
|
|
if (name_atom == JS_ATOM_NULL)
|
|
name_atom = JS_ATOM_empty_string;
|
|
js_function_set_properties(ctx, func_obj, name_atom,
|
|
b->defined_arg_count);
|
|
if (b->func_kind & JS_FUNC_GENERATOR) {
|
|
JSValue proto;
|
|
int proto_class_id;
|
|
/* generators have a prototype field which is used as
|
|
prototype for the generator object */
|
|
if (b->func_kind == JS_FUNC_ASYNC_GENERATOR)
|
|
proto_class_id = JS_CLASS_ASYNC_GENERATOR;
|
|
else
|
|
proto_class_id = JS_CLASS_GENERATOR;
|
|
proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]);
|
|
if (JS_IsException(proto))
|
|
goto fail;
|
|
JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto,
|
|
JS_PROP_WRITABLE);
|
|
} else if (b->has_prototype) {
|
|
/* add the 'prototype' property: delay instantiation to avoid
|
|
creating cycles for every javascript function. The prototype
|
|
object is created on the fly when first accessed */
|
|
JS_SetConstructorBit(ctx, func_obj, TRUE);
|
|
JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype,
|
|
JS_AUTOINIT_ID_PROTOTYPE, NULL,
|
|
JS_PROP_WRITABLE);
|
|
}
|
|
return func_obj;
|
|
fail:
|
|
/* bfunc is freed when func_obj is freed */
|
|
JS_FreeValue(ctx, func_obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
|
|
JSValueConst this_obj,
|
|
int argc, JSValueConst *argv, int flags)
|
|
{
|
|
JSRuntime *rt = ctx->rt;
|
|
JSCFunctionType func;
|
|
JSObject *p;
|
|
JSStackFrame sf_s, *sf = &sf_s, *prev_sf;
|
|
JSValue ret_val;
|
|
JSValueConst *arg_buf;
|
|
int arg_count, i;
|
|
JSCFunctionEnum cproto;
|
|
p = JS_VALUE_GET_OBJ(func_obj);
|
|
cproto = p->u.cfunc.cproto;
|
|
arg_count = p->u.cfunc.length;
|
|
/* better to always check stack overflow */
|
|
if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count))
|
|
return JS_ThrowStackOverflow(ctx);
|
|
prev_sf = rt->current_stack_frame;
|
|
sf->prev_frame = prev_sf;
|
|
rt->current_stack_frame = sf;
|
|
ctx = p->u.cfunc.realm; /* change the current realm */
|
|
#ifdef CONFIG_BIGNUM
|
|
/* we only propagate the bignum mode as some runtime functions
|
|
test it */
|
|
if (prev_sf)
|
|
sf->js_mode = prev_sf->js_mode & JS_MODE_MATH;
|
|
else
|
|
sf->js_mode = 0;
|
|
#else
|
|
sf->js_mode = 0;
|
|
#endif
|
|
sf->cur_func = (JSValue)func_obj;
|
|
sf->arg_count = argc;
|
|
arg_buf = argv;
|
|
if (UNLIKELY(argc < arg_count)) {
|
|
/* ensure that at least argc_count arguments are readable */
|
|
arg_buf = gc(malloc(sizeof(arg_buf[0]) * arg_count));
|
|
for(i = 0; i < argc; i++)
|
|
arg_buf[i] = argv[i];
|
|
for(i = argc; i < arg_count; i++)
|
|
arg_buf[i] = JS_UNDEFINED;
|
|
sf->arg_count = arg_count;
|
|
}
|
|
sf->arg_buf = (JSValue*)arg_buf;
|
|
func = p->u.cfunc.c_function;
|
|
switch(cproto) {
|
|
case JS_CFUNC_constructor:
|
|
case JS_CFUNC_constructor_or_func:
|
|
if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {
|
|
if (cproto == JS_CFUNC_constructor) {
|
|
not_a_constructor:
|
|
ret_val = JS_ThrowTypeError(ctx, "must be called with new");
|
|
break;
|
|
} else {
|
|
this_obj = JS_UNDEFINED;
|
|
}
|
|
}
|
|
/* here this_obj is new_target */
|
|
/* fall thru */
|
|
case JS_CFUNC_generic:
|
|
ret_val = func.generic(ctx, this_obj, argc, arg_buf);
|
|
break;
|
|
case JS_CFUNC_constructor_magic:
|
|
case JS_CFUNC_constructor_or_func_magic:
|
|
if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {
|
|
if (cproto == JS_CFUNC_constructor_magic) {
|
|
goto not_a_constructor;
|
|
} else {
|
|
this_obj = JS_UNDEFINED;
|
|
}
|
|
}
|
|
/* fall thru */
|
|
case JS_CFUNC_generic_magic:
|
|
ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf,
|
|
p->u.cfunc.magic);
|
|
break;
|
|
case JS_CFUNC_getter:
|
|
ret_val = func.getter(ctx, this_obj);
|
|
break;
|
|
case JS_CFUNC_setter:
|
|
ret_val = func.setter(ctx, this_obj, arg_buf[0]);
|
|
break;
|
|
case JS_CFUNC_getter_magic:
|
|
ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic);
|
|
break;
|
|
case JS_CFUNC_setter_magic:
|
|
ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic);
|
|
break;
|
|
case JS_CFUNC_f_f:
|
|
{
|
|
double d1;
|
|
if (UNLIKELY(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {
|
|
ret_val = JS_EXCEPTION;
|
|
break;
|
|
}
|
|
ret_val = JS_NewFloat64(ctx, func.f_f(d1));
|
|
}
|
|
break;
|
|
case JS_CFUNC_f_f_f:
|
|
{
|
|
double d1, d2;
|
|
if (UNLIKELY(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {
|
|
ret_val = JS_EXCEPTION;
|
|
break;
|
|
}
|
|
if (UNLIKELY(JS_ToFloat64(ctx, &d2, arg_buf[1]))) {
|
|
ret_val = JS_EXCEPTION;
|
|
break;
|
|
}
|
|
ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2));
|
|
}
|
|
break;
|
|
case JS_CFUNC_iterator_next:
|
|
{
|
|
int done;
|
|
ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf,
|
|
&done, p->u.cfunc.magic);
|
|
if (!JS_IsException(ret_val) && done != 2) {
|
|
ret_val = js_create_iterator_result(ctx, ret_val, done);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
rt->current_stack_frame = sf->prev_frame;
|
|
return ret_val;
|
|
}
|
|
|
|
JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
|
|
JSValueConst this_obj,
|
|
int argc, JSValueConst *argv, int flags)
|
|
{
|
|
JSObject *p;
|
|
JSBoundFunction *bf;
|
|
JSValueConst *arg_buf, new_target;
|
|
int arg_count, i;
|
|
p = JS_VALUE_GET_OBJ(func_obj);
|
|
bf = p->u.bound_function;
|
|
arg_count = bf->argc + argc;
|
|
if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count))
|
|
return JS_ThrowStackOverflow(ctx);
|
|
arg_buf = gc(malloc(sizeof(JSValue) * arg_count));
|
|
for(i = 0; i < bf->argc; i++) {
|
|
arg_buf[i] = bf->argv[i];
|
|
}
|
|
for(i = 0; i < argc; i++) {
|
|
arg_buf[bf->argc + i] = argv[i];
|
|
}
|
|
if (flags & JS_CALL_FLAG_CONSTRUCTOR) {
|
|
new_target = this_obj;
|
|
if (js_same_value(ctx, func_obj, new_target))
|
|
new_target = bf->func_obj;
|
|
return JS_CallConstructor2(ctx, bf->func_obj, new_target,
|
|
arg_count, arg_buf);
|
|
} else {
|
|
return JS_Call(ctx, bf->func_obj, bf->this_val,
|
|
arg_count, arg_buf);
|
|
}
|
|
}
|
|
|
|
JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,
|
|
JSValueConst new_target,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
return JS_CallConstructorInternal(ctx, func_obj, new_target,
|
|
argc, (JSValue *)argv,
|
|
JS_CALL_FLAG_COPY_ARGV);
|
|
}
|
|
|
|
JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
return JS_CallConstructorInternal(ctx, func_obj, func_obj,
|
|
argc, (JSValue *)argv,
|
|
JS_CALL_FLAG_COPY_ARGV);
|
|
}
|
|
|
|
JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue func_obj;
|
|
func_obj = JS_GetProperty(ctx, this_val, atom);
|
|
if (JS_IsException(func_obj))
|
|
return func_obj;
|
|
return JS_CallFree(ctx, func_obj, this_val, argc, argv);
|
|
}
|
|
|
|
JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv);
|
|
JS_FreeValue(ctx, this_val);
|
|
return res;
|
|
}
|
|
|
|
static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv)
|
|
{
|
|
JSValue val, *tab;
|
|
JSProperty *pr;
|
|
JSObject *p;
|
|
int i;
|
|
val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
|
|
JS_CLASS_ARGUMENTS);
|
|
if (JS_IsException(val))
|
|
return val;
|
|
p = JS_VALUE_GET_OBJ(val);
|
|
/* add the length field (cannot fail) */
|
|
pr = add_property(ctx, p, JS_ATOM_length,
|
|
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
|
|
pr->u.value = JS_NewInt32(ctx, argc);
|
|
/* initialize the fast array part */
|
|
tab = NULL;
|
|
if (argc > 0) {
|
|
tab = js_malloc(ctx, sizeof(tab[0]) * argc);
|
|
if (!tab) {
|
|
JS_FreeValue(ctx, val);
|
|
return JS_EXCEPTION;
|
|
}
|
|
for(i = 0; i < argc; i++) {
|
|
tab[i] = JS_DupValue(ctx, argv[i]);
|
|
}
|
|
}
|
|
p->u.array.u.values = tab;
|
|
p->u.array.count = argc;
|
|
JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,
|
|
JS_DupValue(ctx, ctx->array_proto_values),
|
|
JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
|
|
/* add callee property to throw a TypeError in strict mode */
|
|
JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED,
|
|
ctx->throw_type_error, ctx->throw_type_error,
|
|
JS_PROP_HAS_GET | JS_PROP_HAS_SET);
|
|
return val;
|
|
}
|
|
|
|
/* legacy arguments object: add references to the function arguments */
|
|
static JSValue js_build_mapped_arguments(JSContext *ctx, int argc,
|
|
JSValueConst *argv,
|
|
JSStackFrame *sf, int arg_count)
|
|
{
|
|
JSValue val;
|
|
JSProperty *pr;
|
|
JSObject *p;
|
|
int i;
|
|
val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
|
|
JS_CLASS_MAPPED_ARGUMENTS);
|
|
if (JS_IsException(val))
|
|
return val;
|
|
p = JS_VALUE_GET_OBJ(val);
|
|
/* add the length field (cannot fail) */
|
|
pr = add_property(ctx, p, JS_ATOM_length,
|
|
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
|
|
pr->u.value = JS_NewInt32(ctx, argc);
|
|
for(i = 0; i < arg_count; i++) {
|
|
JSVarRef *var_ref;
|
|
var_ref = get_var_ref(ctx, sf, i, TRUE);
|
|
if (!var_ref)
|
|
goto fail;
|
|
pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF);
|
|
if (!pr) {
|
|
free_var_ref(ctx->rt, var_ref);
|
|
goto fail;
|
|
}
|
|
pr->u.var_ref = var_ref;
|
|
}
|
|
/* the arguments not mapped to the arguments of the function can
|
|
be normal properties */
|
|
for(i = arg_count; i < argc; i++) {
|
|
if (JS_DefinePropertyValueUint32(ctx, val, i,
|
|
JS_DupValue(ctx, argv[i]),
|
|
JS_PROP_C_W_E) < 0)
|
|
goto fail;
|
|
}
|
|
JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,
|
|
JS_DupValue(ctx, ctx->array_proto_values),
|
|
JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
|
|
/* callee returns this function in non strict mode */
|
|
JS_DefinePropertyValue(ctx, val, JS_ATOM_callee,
|
|
JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func),
|
|
JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
|
|
return val;
|
|
fail:
|
|
JS_FreeValue(ctx, val);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_import_meta(JSContext *ctx)
|
|
{
|
|
JSAtom filename;
|
|
JSModuleDef *m;
|
|
filename = JS_GetScriptOrModuleName(ctx, 0);
|
|
if (filename == JS_ATOM_NULL)
|
|
goto fail;
|
|
/* XXX: inefficient, need to add a module or script pointer in
|
|
JSFunctionBytecode */
|
|
m = js_find_loaded_module(ctx, filename);
|
|
JS_FreeAtom(ctx, filename);
|
|
if (!m) {
|
|
fail:
|
|
JS_ThrowTypeError(ctx, "import.meta not supported in this context");
|
|
return JS_EXCEPTION;
|
|
}
|
|
return JS_GetImportMeta(ctx, m);
|
|
}
|
|
|
|
static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *argv)
|
|
{
|
|
JSValue val;
|
|
int i, ret;
|
|
val = JS_NewArray(ctx);
|
|
if (JS_IsException(val))
|
|
return val;
|
|
for (i = first; i < argc; i++) {
|
|
ret = JS_DefinePropertyValueUint32(ctx, val, i - first,
|
|
JS_DupValue(ctx, argv[i]),
|
|
JS_PROP_C_W_E);
|
|
if (ret < 0) {
|
|
JS_FreeValue(ctx, val);
|
|
return JS_EXCEPTION;
|
|
}
|
|
}
|
|
return val;
|
|
}
|
|
|
|
static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
|
|
{
|
|
JSObject *p, *p1, *home_obj;
|
|
JSShapeProperty *prs;
|
|
JSProperty *pr;
|
|
JSValueConst brand;
|
|
/* get the home object of 'func' */
|
|
if (UNLIKELY(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) {
|
|
not_obj:
|
|
JS_ThrowTypeErrorNotAnObject(ctx);
|
|
return -1;
|
|
}
|
|
p1 = JS_VALUE_GET_OBJ(func);
|
|
if (!js_class_has_bytecode(p1->class_id))
|
|
goto not_obj;
|
|
home_obj = p1->u.func.home_object;
|
|
if (!home_obj)
|
|
goto not_obj;
|
|
prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand);
|
|
if (!prs) {
|
|
JS_ThrowTypeError(ctx, "expecting <brand> private field");
|
|
return -1;
|
|
}
|
|
brand = pr->u.value;
|
|
/* safety check */
|
|
if (UNLIKELY(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL))
|
|
goto not_obj;
|
|
/* get the brand array of 'obj' */
|
|
if (UNLIKELY(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
|
|
goto not_obj;
|
|
p = JS_VALUE_GET_OBJ(obj);
|
|
prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand));
|
|
if (!prs) {
|
|
JS_ThrowTypeError(ctx, "invalid brand on object");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* descr must be a non-numeric string atom */
|
|
static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,
|
|
int atom_type)
|
|
{
|
|
JSRuntime *rt = ctx->rt;
|
|
JSString *p;
|
|
assert(!__JS_AtomIsTaggedInt(descr));
|
|
assert(descr < rt->atom_size);
|
|
p = rt->atom_array[descr];
|
|
JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
|
|
return JS_NewSymbol(ctx, p, atom_type);
|
|
}
|
|
|
|
static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)
|
|
{
|
|
JSObject *p, *p1;
|
|
JSShapeProperty *prs;
|
|
JSProperty *pr;
|
|
JSValue brand;
|
|
JSAtom brand_atom;
|
|
if (UNLIKELY(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) {
|
|
JS_ThrowTypeErrorNotAnObject(ctx);
|
|
return -1;
|
|
}
|
|
p = JS_VALUE_GET_OBJ(home_obj);
|
|
prs = find_own_property(&pr, p, JS_ATOM_Private_brand);
|
|
if (!prs) {
|
|
brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE);
|
|
if (JS_IsException(brand))
|
|
return -1;
|
|
/* if the brand is not present, add it */
|
|
pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E);
|
|
if (!pr) {
|
|
JS_FreeValue(ctx, brand);
|
|
return -1;
|
|
}
|
|
pr->u.value = JS_DupValue(ctx, brand);
|
|
} else {
|
|
brand = JS_DupValue(ctx, pr->u.value);
|
|
}
|
|
brand_atom = js_symbol_to_atom(ctx, brand);
|
|
if (UNLIKELY(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
|
|
JS_ThrowTypeErrorNotAnObject(ctx);
|
|
JS_FreeAtom(ctx, brand_atom);
|
|
return -1;
|
|
}
|
|
p1 = JS_VALUE_GET_OBJ(obj);
|
|
pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E);
|
|
JS_FreeAtom(ctx, brand_atom);
|
|
if (!pr)
|
|
return -1;
|
|
pr->u.value = JS_UNDEFINED;
|
|
return 0;
|
|
}
|
|
|
|
static JSValue js_dynamic_import_job(JSContext *ctx,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValueConst *resolving_funcs = argv;
|
|
JSValueConst basename_val = argv[2];
|
|
JSValueConst specifier = argv[3];
|
|
JSModuleDef *m;
|
|
const char *basename = NULL, *filename;
|
|
JSValue ret, err, ns;
|
|
if (!JS_IsString(basename_val)) {
|
|
JS_ThrowTypeError(ctx, "no function filename for import()");
|
|
goto exception;
|
|
}
|
|
basename = JS_ToCString(ctx, basename_val);
|
|
if (!basename)
|
|
goto exception;
|
|
filename = JS_ToCString(ctx, specifier);
|
|
if (!filename)
|
|
goto exception;
|
|
m = JS_RunModule(ctx, basename, filename);
|
|
JS_FreeCString(ctx, filename);
|
|
if (!m)
|
|
goto exception;
|
|
/* return the module namespace */
|
|
ns = js_get_module_ns(ctx, m);
|
|
if (JS_IsException(ns))
|
|
goto exception;
|
|
ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,
|
|
1, (JSValueConst *)&ns);
|
|
JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
|
|
JS_FreeValue(ctx, ns);
|
|
JS_FreeCString(ctx, basename);
|
|
return JS_UNDEFINED;
|
|
exception:
|
|
err = JS_GetException(ctx);
|
|
ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
|
|
1, (JSValueConst *)&err);
|
|
JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
|
|
JS_FreeValue(ctx, err);
|
|
JS_FreeCString(ctx, basename);
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier)
|
|
{
|
|
JSAtom basename;
|
|
JSValue promise, resolving_funcs[2], basename_val;
|
|
JSValueConst args[4];
|
|
basename = JS_GetScriptOrModuleName(ctx, 0);
|
|
if (basename == JS_ATOM_NULL)
|
|
basename_val = JS_NULL;
|
|
else
|
|
basename_val = JS_AtomToValue(ctx, basename);
|
|
JS_FreeAtom(ctx, basename);
|
|
if (JS_IsException(basename_val))
|
|
return basename_val;
|
|
promise = JS_NewPromiseCapability(ctx, resolving_funcs);
|
|
if (JS_IsException(promise)) {
|
|
JS_FreeValue(ctx, basename_val);
|
|
return promise;
|
|
}
|
|
args[0] = resolving_funcs[0];
|
|
args[1] = resolving_funcs[1];
|
|
args[2] = basename_val;
|
|
args[3] = specifier;
|
|
JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args);
|
|
JS_FreeValue(ctx, basename_val);
|
|
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
JS_FreeValue(ctx, resolving_funcs[1]);
|
|
return promise;
|
|
}
|
|
|
|
/* use for strict variable access: test if the variable exists */
|
|
static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop)
|
|
{
|
|
JSObject *p;
|
|
JSShapeProperty *prs;
|
|
int ret;
|
|
/* no exotic behavior is possible in global_var_obj */
|
|
p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
|
|
prs = find_own_property1(p, prop);
|
|
if (prs) {
|
|
ret = TRUE;
|
|
} else {
|
|
ret = JS_HasProperty(ctx, ctx->global_obj, prop);
|
|
if (ret < 0)
|
|
return -1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,
|
|
BOOL throw_ref_error)
|
|
{
|
|
JSObject *p;
|
|
JSShapeProperty *prs;
|
|
JSProperty *pr;
|
|
/* no exotic behavior is possible in global_var_obj */
|
|
p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
|
|
prs = find_own_property(&pr, p, prop);
|
|
if (prs) {
|
|
/* XXX: should handle JS_PROP_TMASK properties */
|
|
if (UNLIKELY(JS_IsUninitialized(pr->u.value)))
|
|
return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
|
|
return JS_DupValue(ctx, pr->u.value);
|
|
}
|
|
return JS_GetPropertyInternal(ctx, ctx->global_obj, prop,
|
|
ctx->global_obj, throw_ref_error);
|
|
}
|
|
|
|
/* flag = 0: normal variable write
|
|
flag = 1: initialize lexical variable
|
|
flag = 2: normal variable write, strict check was done before
|
|
*/
|
|
static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,
|
|
int flag)
|
|
{
|
|
JSObject *p;
|
|
JSShapeProperty *prs;
|
|
JSProperty *pr;
|
|
int flags;
|
|
/* no exotic behavior is possible in global_var_obj */
|
|
p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
|
|
prs = find_own_property(&pr, p, prop);
|
|
if (prs) {
|
|
/* XXX: should handle JS_PROP_AUTOINIT properties? */
|
|
if (flag != 1) {
|
|
if (UNLIKELY(JS_IsUninitialized(pr->u.value))) {
|
|
JS_FreeValue(ctx, val);
|
|
JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
|
|
return -1;
|
|
}
|
|
if (UNLIKELY(!(prs->flags & JS_PROP_WRITABLE))) {
|
|
JS_FreeValue(ctx, val);
|
|
return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);
|
|
}
|
|
}
|
|
set_value(ctx, &pr->u.value, val);
|
|
return 0;
|
|
}
|
|
flags = JS_PROP_THROW_STRICT;
|
|
if (is_strict_mode(ctx))
|
|
flags |= JS_PROP_NO_ADD;
|
|
return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags);
|
|
}
|
|
|
|
/* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */
|
|
/* XXX: could support exotic global object. */
|
|
static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags)
|
|
{
|
|
JSObject *p;
|
|
JSShapeProperty *prs;
|
|
p = JS_VALUE_GET_OBJ(ctx->global_obj);
|
|
prs = find_own_property1(p, prop);
|
|
/* XXX: should handle JS_PROP_AUTOINIT */
|
|
if (flags & DEFINE_GLOBAL_LEX_VAR) {
|
|
if (prs && !(prs->flags & JS_PROP_CONFIGURABLE))
|
|
goto fail_redeclaration;
|
|
} else {
|
|
if (!prs && !p->extensible)
|
|
goto define_error;
|
|
if (flags & DEFINE_GLOBAL_FUNC_VAR) {
|
|
if (prs) {
|
|
if (!(prs->flags & JS_PROP_CONFIGURABLE) &&
|
|
((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET ||
|
|
((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) !=
|
|
(JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) {
|
|
define_error:
|
|
JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'",
|
|
prop);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* check if there already is a lexical declaration */
|
|
p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
|
|
prs = find_own_property1(p, prop);
|
|
if (prs) {
|
|
fail_redeclaration:
|
|
JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) |
|
|
JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */
|
|
/* XXX: could support exotic global object. */
|
|
static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags)
|
|
{
|
|
JSObject *p;
|
|
JSShapeProperty *prs;
|
|
JSProperty *pr;
|
|
JSValue val;
|
|
int flags;
|
|
if (def_flags & DEFINE_GLOBAL_LEX_VAR) {
|
|
p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
|
|
flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) |
|
|
JS_PROP_CONFIGURABLE;
|
|
val = JS_UNINITIALIZED;
|
|
} else {
|
|
p = JS_VALUE_GET_OBJ(ctx->global_obj);
|
|
flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |
|
|
(def_flags & JS_PROP_CONFIGURABLE);
|
|
val = JS_UNDEFINED;
|
|
}
|
|
prs = find_own_property1(p, prop);
|
|
if (prs)
|
|
return 0;
|
|
if (!p->extensible)
|
|
return 0;
|
|
pr = add_property(ctx, p, prop, flags);
|
|
if (UNLIKELY(!pr))
|
|
return -1;
|
|
pr->u.value = val;
|
|
return 0;
|
|
}
|
|
|
|
/* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */
|
|
/* XXX: could support exotic global object. */
|
|
static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop,
|
|
JSValueConst func, int def_flags)
|
|
{
|
|
JSObject *p;
|
|
JSShapeProperty *prs;
|
|
int flags;
|
|
p = JS_VALUE_GET_OBJ(ctx->global_obj);
|
|
prs = find_own_property1(p, prop);
|
|
flags = JS_PROP_HAS_VALUE | JS_PROP_THROW;
|
|
if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) {
|
|
flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags |
|
|
JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE;
|
|
}
|
|
if (JS_DefineProperty(ctx, ctx->global_obj, prop, func,
|
|
JS_UNDEFINED, JS_UNDEFINED, flags) < 0)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg)
|
|
{
|
|
struct list_head *el, *el1;
|
|
JSVarRef *var_ref;
|
|
int var_idx = idx;
|
|
list_for_each_safe(el, el1, &sf->var_ref_list) {
|
|
var_ref = list_entry(el, JSVarRef, header.link);
|
|
if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) {
|
|
var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]);
|
|
var_ref->pvalue = &var_ref->value;
|
|
list_del(&var_ref->header.link);
|
|
/* the reference is no longer to a local variable */
|
|
var_ref->is_detached = TRUE;
|
|
add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* construct a reference to a global variable */
|
|
static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp)
|
|
{
|
|
JSObject *p;
|
|
JSShapeProperty *prs;
|
|
JSProperty *pr;
|
|
/* no exotic behavior is possible in global_var_obj */
|
|
p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
|
|
prs = find_own_property(&pr, p, prop);
|
|
if (prs) {
|
|
/* XXX: should handle JS_PROP_AUTOINIT properties? */
|
|
/* XXX: conformance: do these tests in
|
|
OP_put_var_ref/OP_get_var_ref ? */
|
|
if (UNLIKELY(JS_IsUninitialized(pr->u.value))) {
|
|
JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
|
|
return -1;
|
|
}
|
|
if (UNLIKELY(!(prs->flags & JS_PROP_WRITABLE))) {
|
|
return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);
|
|
}
|
|
sp[0] = JS_DupValue(ctx, ctx->global_var_obj);
|
|
} else {
|
|
int ret;
|
|
ret = JS_HasProperty(ctx, ctx->global_obj, prop);
|
|
if (ret < 0)
|
|
return -1;
|
|
if (ret) {
|
|
sp[0] = JS_DupValue(ctx, ctx->global_obj);
|
|
} else {
|
|
sp[0] = JS_UNDEFINED;
|
|
}
|
|
}
|
|
sp[1] = JS_AtomToValue(ctx, prop);
|
|
return 0;
|
|
}
|
|
|
|
static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)
|
|
{
|
|
JSObject *p;
|
|
JSPropertyEnum *tab_atom;
|
|
int i;
|
|
JSValue enum_obj, obj1;
|
|
JSForInIterator *it;
|
|
uint32_t tag, tab_atom_count;
|
|
tag = JS_VALUE_GET_TAG(obj);
|
|
if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) {
|
|
obj = JS_ToObjectFree(ctx, obj);
|
|
}
|
|
it = js_malloc(ctx, sizeof(*it));
|
|
if (!it) {
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR);
|
|
if (JS_IsException(enum_obj)) {
|
|
js_free(ctx, it);
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
it->is_array = FALSE;
|
|
it->obj = obj;
|
|
it->idx = 0;
|
|
p = JS_VALUE_GET_OBJ(enum_obj);
|
|
p->u.for_in_iterator = it;
|
|
if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
|
|
return enum_obj;
|
|
/* fast path: assume no enumerable properties in the prototype chain */
|
|
obj1 = JS_DupValue(ctx, obj);
|
|
for(;;) {
|
|
obj1 = JS_GetPrototypeFree(ctx, obj1);
|
|
if (JS_IsNull(obj1))
|
|
break;
|
|
if (JS_IsException(obj1))
|
|
goto fail;
|
|
if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
|
|
JS_VALUE_GET_OBJ(obj1),
|
|
JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) {
|
|
JS_FreeValue(ctx, obj1);
|
|
goto fail;
|
|
}
|
|
js_free_prop_enum(ctx, tab_atom, tab_atom_count);
|
|
if (tab_atom_count != 0) {
|
|
JS_FreeValue(ctx, obj1);
|
|
goto slow_path;
|
|
}
|
|
/* must check for timeout to avoid infinite loop */
|
|
if (js_poll_interrupts(ctx)) {
|
|
JS_FreeValue(ctx, obj1);
|
|
goto fail;
|
|
}
|
|
}
|
|
p = JS_VALUE_GET_OBJ(obj);
|
|
if (p->fast_array) {
|
|
JSShape *sh;
|
|
JSShapeProperty *prs;
|
|
/* check that there are no enumerable normal fields */
|
|
sh = p->shape;
|
|
for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
|
|
if (prs->flags & JS_PROP_ENUMERABLE)
|
|
goto normal_case;
|
|
}
|
|
/* for fast arrays, we only store the number of elements */
|
|
it->is_array = TRUE;
|
|
it->array_length = p->u.array.count;
|
|
} else {
|
|
normal_case:
|
|
if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
|
|
JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY))
|
|
goto fail;
|
|
for(i = 0; i < tab_atom_count; i++) {
|
|
JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0);
|
|
}
|
|
js_free_prop_enum(ctx, tab_atom, tab_atom_count);
|
|
}
|
|
return enum_obj;
|
|
slow_path:
|
|
/* non enumerable properties hide the enumerables ones in the
|
|
prototype chain */
|
|
obj1 = JS_DupValue(ctx, obj);
|
|
for(;;) {
|
|
if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
|
|
JS_VALUE_GET_OBJ(obj1),
|
|
JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
|
|
JS_FreeValue(ctx, obj1);
|
|
goto fail;
|
|
}
|
|
for(i = 0; i < tab_atom_count; i++) {
|
|
JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL,
|
|
(tab_atom[i].is_enumerable ?
|
|
JS_PROP_ENUMERABLE : 0));
|
|
}
|
|
js_free_prop_enum(ctx, tab_atom, tab_atom_count);
|
|
obj1 = JS_GetPrototypeFree(ctx, obj1);
|
|
if (JS_IsNull(obj1))
|
|
break;
|
|
if (JS_IsException(obj1))
|
|
goto fail;
|
|
/* must check for timeout to avoid infinite loop */
|
|
if (js_poll_interrupts(ctx)) {
|
|
JS_FreeValue(ctx, obj1);
|
|
goto fail;
|
|
}
|
|
}
|
|
return enum_obj;
|
|
fail:
|
|
JS_FreeValue(ctx, enum_obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
/* obj -> enum_obj */
|
|
static __exception int js_for_in_start(JSContext *ctx, JSValue *sp)
|
|
{
|
|
sp[-1] = build_for_in_iterator(ctx, sp[-1]);
|
|
if (JS_IsException(sp[-1]))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/* enum_obj -> enum_obj value done */
|
|
static __exception int js_for_in_next(JSContext *ctx, JSValue *sp)
|
|
{
|
|
JSValueConst enum_obj;
|
|
JSObject *p;
|
|
JSAtom prop;
|
|
JSForInIterator *it;
|
|
int ret;
|
|
enum_obj = sp[-1];
|
|
/* fail safe */
|
|
if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT)
|
|
goto done;
|
|
p = JS_VALUE_GET_OBJ(enum_obj);
|
|
if (p->class_id != JS_CLASS_FOR_IN_ITERATOR)
|
|
goto done;
|
|
it = p->u.for_in_iterator;
|
|
for(;;) {
|
|
if (it->is_array) {
|
|
if (it->idx >= it->array_length)
|
|
goto done;
|
|
prop = __JS_AtomFromUInt32(it->idx);
|
|
it->idx++;
|
|
} else {
|
|
JSShape *sh = p->shape;
|
|
JSShapeProperty *prs;
|
|
if (it->idx >= sh->prop_count)
|
|
goto done;
|
|
prs = get_shape_prop(sh) + it->idx;
|
|
prop = prs->atom;
|
|
it->idx++;
|
|
if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE))
|
|
continue;
|
|
}
|
|
/* check if the property was deleted */
|
|
ret = JS_HasProperty(ctx, it->obj, prop);
|
|
if (ret < 0)
|
|
return ret;
|
|
if (ret)
|
|
break;
|
|
}
|
|
/* return the property */
|
|
sp[0] = JS_AtomToValue(ctx, prop);
|
|
sp[1] = JS_FALSE;
|
|
return 0;
|
|
done:
|
|
/* return the end */
|
|
sp[0] = JS_UNDEFINED;
|
|
sp[1] = JS_TRUE;
|
|
return 0;
|
|
}
|
|
|
|
/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset'
|
|
objs. If 'done' is true or in case of exception, 'enum_rec' is set
|
|
to undefined. If 'done' is true, 'value' is always set to
|
|
undefined. */
|
|
static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)
|
|
{
|
|
JSValue value = JS_UNDEFINED;
|
|
int done = 1;
|
|
if (LIKELY(!JS_IsUndefined(sp[offset]))) {
|
|
value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done);
|
|
if (JS_IsException(value))
|
|
done = -1;
|
|
if (done) {
|
|
/* value is JS_UNDEFINED or JS_EXCEPTION */
|
|
/* replace the iteration object with undefined */
|
|
JS_FreeValue(ctx, sp[offset]);
|
|
sp[offset] = JS_UNDEFINED;
|
|
if (done < 0) {
|
|
return -1;
|
|
} else {
|
|
JS_FreeValue(ctx, value);
|
|
value = JS_UNDEFINED;
|
|
}
|
|
}
|
|
}
|
|
sp[0] = value;
|
|
sp[1] = JS_NewBool(ctx, done);
|
|
return 0;
|
|
}
|
|
|
|
static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)
|
|
{
|
|
JSValue obj, value;
|
|
BOOL done;
|
|
obj = sp[-1];
|
|
if (!JS_IsObject(obj)) {
|
|
JS_ThrowTypeError(ctx, "iterator must return an object");
|
|
return -1;
|
|
}
|
|
value = JS_IteratorGetCompleteValue(ctx, obj, &done);
|
|
if (JS_IsException(value))
|
|
return -1;
|
|
JS_FreeValue(ctx, obj);
|
|
sp[-1] = value;
|
|
sp[0] = JS_NewBool(ctx, done);
|
|
return 0;
|
|
}
|
|
|
|
static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj,
|
|
JSValueConst name)
|
|
{
|
|
JSObject *p;
|
|
JSShapeProperty *prs;
|
|
JSProperty *pr;
|
|
JSAtom prop;
|
|
if (UNLIKELY(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
|
|
return JS_ThrowTypeErrorNotAnObject(ctx);
|
|
/* safety check */
|
|
if (UNLIKELY(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL))
|
|
return JS_ThrowTypeErrorNotASymbol(ctx);
|
|
prop = js_symbol_to_atom(ctx, (JSValue)name);
|
|
p = JS_VALUE_GET_OBJ(obj);
|
|
prs = find_own_property(&pr, p, prop);
|
|
if (!prs) {
|
|
JS_ThrowTypeErrorPrivateNotFound(ctx, prop);
|
|
return JS_EXCEPTION;
|
|
}
|
|
return JS_DupValue(ctx, pr->u.value);
|
|
}
|
|
|
|
static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj,
|
|
JSValueConst name, JSValue val)
|
|
{
|
|
JSObject *p;
|
|
JSShapeProperty *prs;
|
|
JSProperty *pr;
|
|
JSAtom prop;
|
|
if (UNLIKELY(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
|
|
JS_ThrowTypeErrorNotAnObject(ctx);
|
|
goto fail;
|
|
}
|
|
/* safety check */
|
|
if (UNLIKELY(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {
|
|
JS_ThrowTypeErrorNotASymbol(ctx);
|
|
goto fail;
|
|
}
|
|
prop = js_symbol_to_atom(ctx, (JSValue)name);
|
|
p = JS_VALUE_GET_OBJ(obj);
|
|
prs = find_own_property(&pr, p, prop);
|
|
if (!prs) {
|
|
JS_ThrowTypeErrorPrivateNotFound(ctx, prop);
|
|
fail:
|
|
JS_FreeValue(ctx, val);
|
|
return -1;
|
|
}
|
|
set_value(ctx, &pr->u.value, val);
|
|
return 0;
|
|
}
|
|
|
|
/* Private fields can be added even on non extensible objects or
|
|
Proxies */
|
|
static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj,
|
|
JSValueConst name, JSValue val)
|
|
{
|
|
JSObject *p;
|
|
JSShapeProperty *prs;
|
|
JSProperty *pr;
|
|
JSAtom prop;
|
|
if (UNLIKELY(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
|
|
JS_ThrowTypeErrorNotAnObject(ctx);
|
|
goto fail;
|
|
}
|
|
/* safety check */
|
|
if (UNLIKELY(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {
|
|
JS_ThrowTypeErrorNotASymbol(ctx);
|
|
goto fail;
|
|
}
|
|
prop = js_symbol_to_atom(ctx, (JSValue)name);
|
|
p = JS_VALUE_GET_OBJ(obj);
|
|
prs = find_own_property(&pr, p, prop);
|
|
if (prs) {
|
|
JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists",
|
|
prop);
|
|
goto fail;
|
|
}
|
|
pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
|
|
if (UNLIKELY(!pr)) {
|
|
fail:
|
|
JS_FreeValue(ctx, val);
|
|
return -1;
|
|
}
|
|
pr->u.value = val;
|
|
return 0;
|
|
}
|
|
|
|
/* Modify the name of a method according to the atom and
|
|
'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and
|
|
JS_PROP_HAS_SET. Also set the home object of the method.
|
|
Return < 0 if exception. */
|
|
static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj,
|
|
JSAtom name, int flags, JSValueConst home_obj)
|
|
{
|
|
JSValue name_str;
|
|
name_str = js_get_function_name(ctx, name);
|
|
if (flags & JS_PROP_HAS_GET) {
|
|
name_str = JS_ConcatString3(ctx, "get ", name_str, "");
|
|
} else if (flags & JS_PROP_HAS_SET) {
|
|
name_str = JS_ConcatString3(ctx, "set ", name_str, "");
|
|
}
|
|
if (JS_IsException(name_str))
|
|
return -1;
|
|
if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str,
|
|
JS_PROP_CONFIGURABLE) < 0)
|
|
return -1;
|
|
js_method_set_home_object(ctx, func_obj, home_obj);
|
|
return 0;
|
|
}
|
|
|
|
static int js_op_define_class(JSContext *ctx, JSValue *sp,
|
|
JSAtom class_name, int class_flags,
|
|
JSVarRef **cur_var_refs,
|
|
JSStackFrame *sf, BOOL is_computed_name)
|
|
{
|
|
JSValue bfunc, parent_class, proto = JS_UNDEFINED;
|
|
JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED;
|
|
JSFunctionBytecode *b;
|
|
parent_class = sp[-2];
|
|
bfunc = sp[-1];
|
|
if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) {
|
|
if (JS_IsNull(parent_class)) {
|
|
parent_proto = JS_NULL;
|
|
parent_class = JS_DupValue(ctx, ctx->function_proto);
|
|
} else {
|
|
if (!JS_IsConstructor(ctx, parent_class)) {
|
|
JS_ThrowTypeError(ctx, "parent class must be constructor");
|
|
goto fail;
|
|
}
|
|
parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype);
|
|
if (JS_IsException(parent_proto))
|
|
goto fail;
|
|
if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) {
|
|
JS_ThrowTypeError(ctx, "parent prototype must be an object or null");
|
|
goto fail;
|
|
}
|
|
}
|
|
} else {
|
|
/* parent_class is JS_UNDEFINED in this case */
|
|
parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]);
|
|
parent_class = JS_DupValue(ctx, ctx->function_proto);
|
|
}
|
|
proto = JS_NewObjectProto(ctx, parent_proto);
|
|
if (JS_IsException(proto))
|
|
goto fail;
|
|
b = JS_VALUE_GET_PTR(bfunc);
|
|
assert(b->func_kind == JS_FUNC_NORMAL);
|
|
ctor = JS_NewObjectProtoClass(ctx, parent_class,
|
|
JS_CLASS_BYTECODE_FUNCTION);
|
|
if (JS_IsException(ctor))
|
|
goto fail;
|
|
ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf);
|
|
bfunc = JS_UNDEFINED;
|
|
if (JS_IsException(ctor))
|
|
goto fail;
|
|
js_method_set_home_object(ctx, ctor, proto);
|
|
JS_SetConstructorBit(ctx, ctor, TRUE);
|
|
JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length,
|
|
JS_NewInt32(ctx, b->defined_arg_count),
|
|
JS_PROP_CONFIGURABLE);
|
|
if (is_computed_name) {
|
|
if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3],
|
|
JS_PROP_CONFIGURABLE) < 0)
|
|
goto fail;
|
|
} else {
|
|
if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0)
|
|
goto fail;
|
|
}
|
|
/* the constructor property must be first. It can be overriden by
|
|
computed property names */
|
|
if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,
|
|
JS_DupValue(ctx, ctor),
|
|
JS_PROP_CONFIGURABLE |
|
|
JS_PROP_WRITABLE | JS_PROP_THROW) < 0)
|
|
goto fail;
|
|
/* set the prototype property */
|
|
if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype,
|
|
JS_DupValue(ctx, proto), JS_PROP_THROW) < 0)
|
|
goto fail;
|
|
set_cycle_flag(ctx, ctor);
|
|
set_cycle_flag(ctx, proto);
|
|
JS_FreeValue(ctx, parent_proto);
|
|
JS_FreeValue(ctx, parent_class);
|
|
sp[-2] = ctor;
|
|
sp[-1] = proto;
|
|
return 0;
|
|
fail:
|
|
JS_FreeValue(ctx, parent_class);
|
|
JS_FreeValue(ctx, parent_proto);
|
|
JS_FreeValue(ctx, bfunc);
|
|
JS_FreeValue(ctx, proto);
|
|
JS_FreeValue(ctx, ctor);
|
|
sp[-2] = JS_UNDEFINED;
|
|
sp[-1] = JS_UNDEFINED;
|
|
return -1;
|
|
}
|
|
|
|
static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp)
|
|
{
|
|
JSValue iterator, enumobj, method, value;
|
|
int is_array_iterator;
|
|
JSValue *arrp;
|
|
uint32_t i, count32, pos;
|
|
if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) {
|
|
JS_ThrowInternalError(ctx, "invalid index for append");
|
|
return -1;
|
|
}
|
|
pos = JS_VALUE_GET_INT(sp[-2]);
|
|
/* XXX: further optimisations:
|
|
- use ctx->array_proto_values?
|
|
- check if array_iterator_prototype next method is built-in and
|
|
avoid constructing actual iterator object?
|
|
- build this into js_for_of_start and use in all `for (x of o)` loops
|
|
*/
|
|
iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator);
|
|
if (JS_IsException(iterator))
|
|
return -1;
|
|
is_array_iterator = JS_IsCFunction(ctx, iterator,
|
|
(JSCFunction *)js_create_array_iterator,
|
|
JS_ITERATOR_KIND_VALUE);
|
|
JS_FreeValue(ctx, iterator);
|
|
enumobj = JS_GetIterator(ctx, sp[-1], FALSE);
|
|
if (JS_IsException(enumobj))
|
|
return -1;
|
|
method = JS_GetProperty(ctx, enumobj, JS_ATOM_next);
|
|
if (JS_IsException(method)) {
|
|
JS_FreeValue(ctx, enumobj);
|
|
return -1;
|
|
}
|
|
if (is_array_iterator
|
|
&& JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0)
|
|
&& js_get_fast_array(ctx, sp[-1], &arrp, &count32)) {
|
|
uint32_t len;
|
|
if (js_get_length32(ctx, &len, sp[-1]))
|
|
goto exception;
|
|
/* if len > count32, the elements >= count32 might be read in
|
|
the prototypes and might have side effects */
|
|
if (len != count32)
|
|
goto general_case;
|
|
/* Handle fast arrays explicitly */
|
|
for (i = 0; i < count32; i++) {
|
|
if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++,
|
|
JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0)
|
|
goto exception;
|
|
}
|
|
} else {
|
|
general_case:
|
|
for (;;) {
|
|
BOOL done;
|
|
value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done);
|
|
if (JS_IsException(value))
|
|
goto exception;
|
|
if (done) {
|
|
/* value is JS_UNDEFINED */
|
|
break;
|
|
}
|
|
if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0)
|
|
goto exception;
|
|
}
|
|
}
|
|
/* Note: could raise an error if too many elements */
|
|
sp[-2] = JS_NewInt32(ctx, pos);
|
|
JS_FreeValue(ctx, enumobj);
|
|
JS_FreeValue(ctx, method);
|
|
return 0;
|
|
exception:
|
|
JS_IteratorClose(ctx, enumobj, TRUE);
|
|
JS_FreeValue(ctx, enumobj);
|
|
JS_FreeValue(ctx, method);
|
|
return -1;
|
|
}
|
|
|
|
static dontdiscard int js_operator_instanceof(JSContext *ctx, JSValue *sp)
|
|
{
|
|
JSValue op1, op2;
|
|
BOOL ret;
|
|
op1 = sp[-2];
|
|
op2 = sp[-1];
|
|
ret = JS_IsInstanceOf(ctx, op1, op2);
|
|
if (ret < 0)
|
|
return ret;
|
|
JS_FreeValue(ctx, op1);
|
|
JS_FreeValue(ctx, op2);
|
|
sp[-2] = JS_NewBool(ctx, ret);
|
|
return 0;
|
|
}
|
|
|
|
static dontdiscard int js_operator_typeof(JSContext *ctx, JSValueConst op1)
|
|
{
|
|
JSAtom atom;
|
|
uint32_t tag;
|
|
tag = JS_VALUE_GET_NORM_TAG(op1);
|
|
switch(tag) {
|
|
#ifdef CONFIG_BIGNUM
|
|
case JS_TAG_BIG_INT:
|
|
atom = JS_ATOM_bigint;
|
|
break;
|
|
case JS_TAG_BIG_FLOAT:
|
|
atom = JS_ATOM_bigfloat;
|
|
break;
|
|
case JS_TAG_BIG_DECIMAL:
|
|
atom = JS_ATOM_bigdecimal;
|
|
break;
|
|
#endif
|
|
case JS_TAG_INT:
|
|
case JS_TAG_FLOAT64:
|
|
atom = JS_ATOM_number;
|
|
break;
|
|
case JS_TAG_UNDEFINED:
|
|
atom = JS_ATOM_undefined;
|
|
break;
|
|
case JS_TAG_BOOL:
|
|
atom = JS_ATOM_boolean;
|
|
break;
|
|
case JS_TAG_STRING:
|
|
atom = JS_ATOM_string;
|
|
break;
|
|
case JS_TAG_OBJECT:
|
|
{
|
|
JSObject *p;
|
|
p = JS_VALUE_GET_OBJ(op1);
|
|
if (UNLIKELY(p->is_HTMLDDA))
|
|
atom = JS_ATOM_undefined;
|
|
else if (JS_IsFunction(ctx, op1))
|
|
atom = JS_ATOM_function;
|
|
else
|
|
goto obj_type;
|
|
}
|
|
break;
|
|
case JS_TAG_NULL:
|
|
obj_type:
|
|
atom = JS_ATOM_object;
|
|
break;
|
|
case JS_TAG_SYMBOL:
|
|
atom = JS_ATOM_symbol;
|
|
break;
|
|
default:
|
|
atom = JS_ATOM_unknown;
|
|
break;
|
|
}
|
|
return atom;
|
|
}
|
|
|
|
static dontdiscard int js_operator_delete(JSContext *ctx, JSValue *sp)
|
|
{
|
|
JSValue op1, op2;
|
|
JSAtom atom;
|
|
int ret;
|
|
op1 = sp[-2];
|
|
op2 = sp[-1];
|
|
atom = JS_ValueToAtom(ctx, op2);
|
|
if (UNLIKELY(atom == JS_ATOM_NULL))
|
|
return -1;
|
|
ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT);
|
|
JS_FreeAtom(ctx, atom);
|
|
if (UNLIKELY(ret < 0))
|
|
return -1;
|
|
JS_FreeValue(ctx, op1);
|
|
JS_FreeValue(ctx, op2);
|
|
sp[-2] = JS_NewBool(ctx, ret);
|
|
return 0;
|
|
}
|
|
|
|
static dontdiscard int js_has_unscopable(JSContext *ctx, JSValueConst obj, JSAtom atom)
|
|
{
|
|
JSValue arr, val;
|
|
int ret;
|
|
arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables);
|
|
if (JS_IsException(arr))
|
|
return -1;
|
|
ret = 0;
|
|
if (JS_IsObject(arr)) {
|
|
val = JS_GetProperty(ctx, arr, atom);
|
|
ret = JS_ToBoolFree(ctx, val);
|
|
}
|
|
JS_FreeValue(ctx, arr);
|
|
return ret;
|
|
}
|
|
|
|
/* Note: it is important that no exception is returned by this function */
|
|
static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj)
|
|
{
|
|
JSObject *p;
|
|
if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
|
|
return FALSE;
|
|
p = JS_VALUE_GET_OBJ(obj);
|
|
if (p->class_id != JS_CLASS_ERROR)
|
|
return FALSE;
|
|
if (find_own_property1(p, JS_ATOM_stack))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
/* used to avoid catching interrupt exceptions */
|
|
static BOOL JS_IsUncatchableError(JSContext *ctx, JSValueConst val)
|
|
{
|
|
JSObject *p;
|
|
if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
|
|
return FALSE;
|
|
p = JS_VALUE_GET_OBJ(val);
|
|
return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error;
|
|
}
|
|
|
|
static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)
|
|
{
|
|
struct list_head *el, *el1;
|
|
JSVarRef *var_ref;
|
|
int var_idx;
|
|
list_for_each_safe(el, el1, &sf->var_ref_list) {
|
|
var_ref = list_entry(el, JSVarRef, header.link);
|
|
var_idx = var_ref->var_idx;
|
|
if (var_ref->is_arg)
|
|
var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]);
|
|
else
|
|
var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]);
|
|
var_ref->pvalue = &var_ref->value;
|
|
/* the reference is no longer to a local variable */
|
|
var_ref->is_detached = TRUE;
|
|
add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
|
|
}
|
|
}
|
|
|
|
/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
|
|
static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
|
|
JSValueConst this_obj, JSValueConst new_target,
|
|
int argc, JSValue *argv, int flags)
|
|
{
|
|
JSRuntime *rt = caller_ctx->rt;
|
|
JSContext *ctx;
|
|
JSObject *p;
|
|
JSFunctionBytecode *b;
|
|
JSStackFrame sf_s, *sf = &sf_s;
|
|
const uint8_t *pc;
|
|
int opcode, arg_allocated_size, i;
|
|
JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval;
|
|
JSVarRef **var_refs;
|
|
size_t alloca_size;
|
|
#if !DIRECT_DISPATCH
|
|
#define SWITCH(pc) switch (opcode = *pc++)
|
|
#define CASE(op) case op
|
|
#define DEFAULT default
|
|
#define BREAK break
|
|
#else
|
|
static const void * const dispatch_table[256] = {
|
|
#define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id,
|
|
#if SHORT_OPCODES
|
|
#define def(id, size, n_pop, n_push, f)
|
|
#else
|
|
#define def(id, size, n_pop, n_push, f) && case_default,
|
|
#endif
|
|
#include "third_party/quickjs/quickjs-opcode.inc"
|
|
[ OP_COUNT ... 255 ] = &&case_default
|
|
};
|
|
#define SWITCH(pc) goto *dispatch_table[opcode = *pc++];
|
|
#define CASE(op) case_ ## op
|
|
#define DEFAULT case_default
|
|
#define BREAK SWITCH(pc)
|
|
#endif
|
|
if (js_poll_interrupts(caller_ctx))
|
|
return JS_EXCEPTION;
|
|
if (UNLIKELY(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) {
|
|
if (flags & JS_CALL_FLAG_GENERATOR) {
|
|
JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj);
|
|
/* func_obj get contains a pointer to JSFuncAsyncState */
|
|
/* the stack frame is already allocated */
|
|
sf = &s->frame;
|
|
p = JS_VALUE_GET_OBJ(sf->cur_func);
|
|
b = p->u.func.function_bytecode;
|
|
ctx = b->realm;
|
|
var_refs = p->u.func.var_refs;
|
|
local_buf = arg_buf = sf->arg_buf;
|
|
var_buf = sf->var_buf;
|
|
stack_buf = sf->var_buf + b->var_count;
|
|
sp = sf->cur_sp;
|
|
sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */
|
|
pc = sf->cur_pc;
|
|
sf->prev_frame = rt->current_stack_frame;
|
|
rt->current_stack_frame = sf;
|
|
if (s->throw_flag)
|
|
goto exception;
|
|
else
|
|
goto restart;
|
|
} else {
|
|
goto not_a_function;
|
|
}
|
|
}
|
|
p = JS_VALUE_GET_OBJ(func_obj);
|
|
if (UNLIKELY(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
|
|
JSClassCall *call_func;
|
|
call_func = rt->class_array[p->class_id].call;
|
|
if (!call_func) {
|
|
not_a_function:
|
|
return JS_ThrowTypeError(caller_ctx, "not a function");
|
|
}
|
|
return call_func(caller_ctx, func_obj, this_obj, argc,
|
|
(JSValueConst *)argv, flags);
|
|
}
|
|
b = p->u.func.function_bytecode;
|
|
if (UNLIKELY(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) {
|
|
arg_allocated_size = b->arg_count;
|
|
} else {
|
|
arg_allocated_size = 0;
|
|
}
|
|
alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count +
|
|
b->stack_size);
|
|
if (js_check_stack_overflow(rt, alloca_size))
|
|
return JS_ThrowStackOverflow(caller_ctx);
|
|
sf->js_mode = b->js_mode;
|
|
arg_buf = argv;
|
|
sf->arg_count = argc;
|
|
sf->cur_func = (JSValue)func_obj;
|
|
init_list_head(&sf->var_ref_list);
|
|
var_refs = p->u.func.var_refs;
|
|
local_buf = gc(malloc(alloca_size));
|
|
if (UNLIKELY(arg_allocated_size)) {
|
|
int n = min_int(argc, b->arg_count);
|
|
arg_buf = local_buf;
|
|
for(i = 0; i < n; i++)
|
|
arg_buf[i] = JS_DupValue(caller_ctx, argv[i]);
|
|
for(; i < b->arg_count; i++)
|
|
arg_buf[i] = JS_UNDEFINED;
|
|
sf->arg_count = b->arg_count;
|
|
}
|
|
var_buf = local_buf + arg_allocated_size;
|
|
sf->var_buf = var_buf;
|
|
sf->arg_buf = arg_buf;
|
|
for(i = 0; i < b->var_count; i++)
|
|
var_buf[i] = JS_UNDEFINED;
|
|
stack_buf = var_buf + b->var_count;
|
|
sp = stack_buf;
|
|
pc = b->byte_code_buf;
|
|
sf->prev_frame = rt->current_stack_frame;
|
|
rt->current_stack_frame = sf;
|
|
ctx = b->realm; /* set the current realm */
|
|
restart:
|
|
for(;;) {
|
|
int call_argc;
|
|
JSValue *call_argv;
|
|
SWITCH(pc) {
|
|
CASE(OP_push_i32):
|
|
*sp++ = JS_NewInt32(ctx, get_u32(pc));
|
|
pc += 4;
|
|
BREAK;
|
|
CASE(OP_push_const):
|
|
*sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
|
|
pc += 4;
|
|
BREAK;
|
|
#if SHORT_OPCODES
|
|
CASE(OP_push_minus1):
|
|
CASE(OP_push_0):
|
|
CASE(OP_push_1):
|
|
CASE(OP_push_2):
|
|
CASE(OP_push_3):
|
|
CASE(OP_push_4):
|
|
CASE(OP_push_5):
|
|
CASE(OP_push_6):
|
|
CASE(OP_push_7):
|
|
*sp++ = JS_NewInt32(ctx, opcode - OP_push_0);
|
|
BREAK;
|
|
CASE(OP_push_i8):
|
|
*sp++ = JS_NewInt32(ctx, get_i8(pc));
|
|
pc += 1;
|
|
BREAK;
|
|
CASE(OP_push_i16):
|
|
*sp++ = JS_NewInt32(ctx, get_i16(pc));
|
|
pc += 2;
|
|
BREAK;
|
|
CASE(OP_push_const8):
|
|
*sp++ = JS_DupValue(ctx, b->cpool[*pc++]);
|
|
BREAK;
|
|
CASE(OP_fclosure8):
|
|
*sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf);
|
|
if (UNLIKELY(JS_IsException(sp[-1])))
|
|
goto exception;
|
|
BREAK;
|
|
CASE(OP_push_empty_string):
|
|
*sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string);
|
|
BREAK;
|
|
CASE(OP_get_length):
|
|
{
|
|
JSValue val;
|
|
val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length);
|
|
if (UNLIKELY(JS_IsException(val)))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp[-1] = val;
|
|
}
|
|
BREAK;
|
|
#endif
|
|
CASE(OP_push_atom_value):
|
|
*sp++ = JS_AtomToValue(ctx, get_u32(pc));
|
|
pc += 4;
|
|
BREAK;
|
|
CASE(OP_undefined):
|
|
*sp++ = JS_UNDEFINED;
|
|
BREAK;
|
|
CASE(OP_null):
|
|
*sp++ = JS_NULL;
|
|
BREAK;
|
|
CASE(OP_push_this):
|
|
/* OP_push_this is only called at the start of a function */
|
|
{
|
|
JSValue val;
|
|
if (!(b->js_mode & JS_MODE_STRICT)) {
|
|
uint32_t tag = JS_VALUE_GET_TAG(this_obj);
|
|
if (LIKELY(tag == JS_TAG_OBJECT))
|
|
goto normal_this;
|
|
if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) {
|
|
val = JS_DupValue(ctx, ctx->global_obj);
|
|
} else {
|
|
val = JS_ToObject(ctx, this_obj);
|
|
if (JS_IsException(val))
|
|
goto exception;
|
|
}
|
|
} else {
|
|
normal_this:
|
|
val = JS_DupValue(ctx, this_obj);
|
|
}
|
|
*sp++ = val;
|
|
}
|
|
BREAK;
|
|
CASE(OP_push_false):
|
|
*sp++ = JS_FALSE;
|
|
BREAK;
|
|
CASE(OP_push_true):
|
|
*sp++ = JS_TRUE;
|
|
BREAK;
|
|
CASE(OP_object):
|
|
*sp++ = JS_NewObject(ctx);
|
|
if (UNLIKELY(JS_IsException(sp[-1])))
|
|
goto exception;
|
|
BREAK;
|
|
CASE(OP_special_object):
|
|
{
|
|
int arg = *pc++;
|
|
switch(arg) {
|
|
case OP_SPECIAL_OBJECT_ARGUMENTS:
|
|
*sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv);
|
|
if (UNLIKELY(JS_IsException(sp[-1])))
|
|
goto exception;
|
|
break;
|
|
case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS:
|
|
*sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv,
|
|
sf, min_int(argc, b->arg_count));
|
|
if (UNLIKELY(JS_IsException(sp[-1])))
|
|
goto exception;
|
|
break;
|
|
case OP_SPECIAL_OBJECT_THIS_FUNC:
|
|
*sp++ = JS_DupValue(ctx, sf->cur_func);
|
|
break;
|
|
case OP_SPECIAL_OBJECT_NEW_TARGET:
|
|
*sp++ = JS_DupValue(ctx, new_target);
|
|
break;
|
|
case OP_SPECIAL_OBJECT_HOME_OBJECT:
|
|
{
|
|
JSObject *p1;
|
|
p1 = p->u.func.home_object;
|
|
if (UNLIKELY(!p1))
|
|
*sp++ = JS_UNDEFINED;
|
|
else
|
|
*sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
|
|
}
|
|
break;
|
|
case OP_SPECIAL_OBJECT_VAR_OBJECT:
|
|
*sp++ = JS_NewObjectProto(ctx, JS_NULL);
|
|
if (UNLIKELY(JS_IsException(sp[-1])))
|
|
goto exception;
|
|
break;
|
|
case OP_SPECIAL_OBJECT_IMPORT_META:
|
|
*sp++ = js_import_meta(ctx);
|
|
if (UNLIKELY(JS_IsException(sp[-1])))
|
|
goto exception;
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_rest):
|
|
{
|
|
int first = get_u16(pc);
|
|
pc += 2;
|
|
*sp++ = js_build_rest(ctx, first, argc, (JSValueConst *)argv);
|
|
if (UNLIKELY(JS_IsException(sp[-1])))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_drop):
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp--;
|
|
BREAK;
|
|
CASE(OP_nip):
|
|
JS_FreeValue(ctx, sp[-2]);
|
|
sp[-2] = sp[-1];
|
|
sp--;
|
|
BREAK;
|
|
CASE(OP_nip1): /* a b c -> b c */
|
|
JS_FreeValue(ctx, sp[-3]);
|
|
sp[-3] = sp[-2];
|
|
sp[-2] = sp[-1];
|
|
sp--;
|
|
BREAK;
|
|
CASE(OP_dup):
|
|
sp[0] = JS_DupValue(ctx, sp[-1]);
|
|
sp++;
|
|
BREAK;
|
|
CASE(OP_dup2): /* a b -> a b a b */
|
|
sp[0] = JS_DupValue(ctx, sp[-2]);
|
|
sp[1] = JS_DupValue(ctx, sp[-1]);
|
|
sp += 2;
|
|
BREAK;
|
|
CASE(OP_dup3): /* a b c -> a b c a b c */
|
|
sp[0] = JS_DupValue(ctx, sp[-3]);
|
|
sp[1] = JS_DupValue(ctx, sp[-2]);
|
|
sp[2] = JS_DupValue(ctx, sp[-1]);
|
|
sp += 3;
|
|
BREAK;
|
|
CASE(OP_dup1): /* a b -> a a b */
|
|
sp[0] = sp[-1];
|
|
sp[-1] = JS_DupValue(ctx, sp[-2]);
|
|
sp++;
|
|
BREAK;
|
|
CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */
|
|
sp[0] = sp[-1];
|
|
sp[-1] = sp[-2];
|
|
sp[-2] = JS_DupValue(ctx, sp[0]);
|
|
sp++;
|
|
BREAK;
|
|
CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */
|
|
sp[0] = sp[-1];
|
|
sp[-1] = sp[-2];
|
|
sp[-2] = sp[-3];
|
|
sp[-3] = JS_DupValue(ctx, sp[0]);
|
|
sp++;
|
|
BREAK;
|
|
CASE(OP_insert4): /* this obj prop a -> a this obj prop a */
|
|
sp[0] = sp[-1];
|
|
sp[-1] = sp[-2];
|
|
sp[-2] = sp[-3];
|
|
sp[-3] = sp[-4];
|
|
sp[-4] = JS_DupValue(ctx, sp[0]);
|
|
sp++;
|
|
BREAK;
|
|
CASE(OP_perm3): /* obj a b -> a obj b (213) */
|
|
{
|
|
JSValue tmp;
|
|
tmp = sp[-2];
|
|
sp[-2] = sp[-3];
|
|
sp[-3] = tmp;
|
|
}
|
|
BREAK;
|
|
CASE(OP_rot3l): /* x a b -> a b x (231) */
|
|
{
|
|
JSValue tmp;
|
|
tmp = sp[-3];
|
|
sp[-3] = sp[-2];
|
|
sp[-2] = sp[-1];
|
|
sp[-1] = tmp;
|
|
}
|
|
BREAK;
|
|
CASE(OP_rot4l): /* x a b c -> a b c x */
|
|
{
|
|
JSValue tmp;
|
|
tmp = sp[-4];
|
|
sp[-4] = sp[-3];
|
|
sp[-3] = sp[-2];
|
|
sp[-2] = sp[-1];
|
|
sp[-1] = tmp;
|
|
}
|
|
BREAK;
|
|
CASE(OP_rot5l): /* x a b c d -> a b c d x */
|
|
{
|
|
JSValue tmp;
|
|
tmp = sp[-5];
|
|
sp[-5] = sp[-4];
|
|
sp[-4] = sp[-3];
|
|
sp[-3] = sp[-2];
|
|
sp[-2] = sp[-1];
|
|
sp[-1] = tmp;
|
|
}
|
|
BREAK;
|
|
CASE(OP_rot3r): /* a b x -> x a b (312) */
|
|
{
|
|
JSValue tmp;
|
|
tmp = sp[-1];
|
|
sp[-1] = sp[-2];
|
|
sp[-2] = sp[-3];
|
|
sp[-3] = tmp;
|
|
}
|
|
BREAK;
|
|
CASE(OP_perm4): /* obj prop a b -> a obj prop b */
|
|
{
|
|
JSValue tmp;
|
|
tmp = sp[-2];
|
|
sp[-2] = sp[-3];
|
|
sp[-3] = sp[-4];
|
|
sp[-4] = tmp;
|
|
}
|
|
BREAK;
|
|
CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */
|
|
{
|
|
JSValue tmp;
|
|
tmp = sp[-2];
|
|
sp[-2] = sp[-3];
|
|
sp[-3] = sp[-4];
|
|
sp[-4] = sp[-5];
|
|
sp[-5] = tmp;
|
|
}
|
|
BREAK;
|
|
CASE(OP_swap): /* a b -> b a */
|
|
{
|
|
JSValue tmp;
|
|
tmp = sp[-2];
|
|
sp[-2] = sp[-1];
|
|
sp[-1] = tmp;
|
|
}
|
|
BREAK;
|
|
CASE(OP_swap2): /* a b c d -> c d a b */
|
|
{
|
|
JSValue tmp1, tmp2;
|
|
tmp1 = sp[-4];
|
|
tmp2 = sp[-3];
|
|
sp[-4] = sp[-2];
|
|
sp[-3] = sp[-1];
|
|
sp[-2] = tmp1;
|
|
sp[-1] = tmp2;
|
|
}
|
|
BREAK;
|
|
CASE(OP_fclosure):
|
|
{
|
|
JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
|
|
pc += 4;
|
|
*sp++ = js_closure(ctx, bfunc, var_refs, sf);
|
|
if (UNLIKELY(JS_IsException(sp[-1])))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
#if SHORT_OPCODES
|
|
CASE(OP_call0):
|
|
CASE(OP_call1):
|
|
CASE(OP_call2):
|
|
CASE(OP_call3):
|
|
call_argc = opcode - OP_call0;
|
|
goto has_call_argc;
|
|
#endif
|
|
CASE(OP_call):
|
|
CASE(OP_tail_call):
|
|
{
|
|
call_argc = get_u16(pc);
|
|
pc += 2;
|
|
goto has_call_argc;
|
|
has_call_argc:
|
|
call_argv = sp - call_argc;
|
|
sf->cur_pc = pc;
|
|
ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,
|
|
JS_UNDEFINED, call_argc, call_argv, 0);
|
|
if (UNLIKELY(JS_IsException(ret_val)))
|
|
goto exception;
|
|
if (opcode == OP_tail_call)
|
|
goto done;
|
|
for(i = -1; i < call_argc; i++)
|
|
JS_FreeValue(ctx, call_argv[i]);
|
|
sp -= call_argc + 1;
|
|
*sp++ = ret_val;
|
|
}
|
|
BREAK;
|
|
CASE(OP_call_constructor):
|
|
{
|
|
call_argc = get_u16(pc);
|
|
pc += 2;
|
|
call_argv = sp - call_argc;
|
|
sf->cur_pc = pc;
|
|
ret_val = JS_CallConstructorInternal(ctx, call_argv[-2],
|
|
call_argv[-1],
|
|
call_argc, call_argv, 0);
|
|
if (UNLIKELY(JS_IsException(ret_val)))
|
|
goto exception;
|
|
for(i = -2; i < call_argc; i++)
|
|
JS_FreeValue(ctx, call_argv[i]);
|
|
sp -= call_argc + 2;
|
|
*sp++ = ret_val;
|
|
}
|
|
BREAK;
|
|
CASE(OP_call_method):
|
|
CASE(OP_tail_call_method):
|
|
{
|
|
call_argc = get_u16(pc);
|
|
pc += 2;
|
|
call_argv = sp - call_argc;
|
|
sf->cur_pc = pc;
|
|
ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2],
|
|
JS_UNDEFINED, call_argc, call_argv, 0);
|
|
if (UNLIKELY(JS_IsException(ret_val)))
|
|
goto exception;
|
|
if (opcode == OP_tail_call_method)
|
|
goto done;
|
|
for(i = -2; i < call_argc; i++)
|
|
JS_FreeValue(ctx, call_argv[i]);
|
|
sp -= call_argc + 2;
|
|
*sp++ = ret_val;
|
|
}
|
|
BREAK;
|
|
CASE(OP_array_from):
|
|
{
|
|
int i, ret;
|
|
call_argc = get_u16(pc);
|
|
pc += 2;
|
|
ret_val = JS_NewArray(ctx);
|
|
if (UNLIKELY(JS_IsException(ret_val)))
|
|
goto exception;
|
|
call_argv = sp - call_argc;
|
|
for(i = 0; i < call_argc; i++) {
|
|
ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i],
|
|
JS_PROP_C_W_E | JS_PROP_THROW);
|
|
call_argv[i] = JS_UNDEFINED;
|
|
if (ret < 0) {
|
|
JS_FreeValue(ctx, ret_val);
|
|
goto exception;
|
|
}
|
|
}
|
|
sp -= call_argc;
|
|
*sp++ = ret_val;
|
|
}
|
|
BREAK;
|
|
CASE(OP_apply):
|
|
{
|
|
int magic;
|
|
magic = get_u16(pc);
|
|
pc += 2;
|
|
ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic);
|
|
if (UNLIKELY(JS_IsException(ret_val)))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-3]);
|
|
JS_FreeValue(ctx, sp[-2]);
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp -= 3;
|
|
*sp++ = ret_val;
|
|
}
|
|
BREAK;
|
|
CASE(OP_return):
|
|
ret_val = *--sp;
|
|
goto done;
|
|
CASE(OP_return_undef):
|
|
ret_val = JS_UNDEFINED;
|
|
goto done;
|
|
CASE(OP_check_ctor_return):
|
|
/* return TRUE if 'this' should be returned */
|
|
if (!JS_IsObject(sp[-1])) {
|
|
if (!JS_IsUndefined(sp[-1])) {
|
|
JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined");
|
|
goto exception;
|
|
}
|
|
sp[0] = JS_TRUE;
|
|
} else {
|
|
sp[0] = JS_FALSE;
|
|
}
|
|
sp++;
|
|
BREAK;
|
|
CASE(OP_check_ctor):
|
|
if (JS_IsUndefined(new_target)) {
|
|
JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'");
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_check_brand):
|
|
if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0)
|
|
goto exception;
|
|
BREAK;
|
|
CASE(OP_add_brand):
|
|
if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0)
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-2]);
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp -= 2;
|
|
BREAK;
|
|
CASE(OP_throw):
|
|
JS_Throw(ctx, *--sp);
|
|
goto exception;
|
|
CASE(OP_throw_error):
|
|
{
|
|
JSAtom atom;
|
|
int type;
|
|
atom = get_u32(pc);
|
|
type = pc[4];
|
|
pc += 5;
|
|
if (type == JS_THROW_VAR_RO)
|
|
JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom);
|
|
else
|
|
if (type == JS_THROW_VAR_REDECL)
|
|
JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom);
|
|
else
|
|
if (type == JS_THROW_VAR_UNINITIALIZED)
|
|
JS_ThrowReferenceErrorUninitialized(ctx, atom);
|
|
else
|
|
if (type == JS_THROW_ERROR_DELETE_SUPER)
|
|
JS_ThrowReferenceError(ctx, "unsupported reference to 'super'");
|
|
else
|
|
if (type == JS_THROW_ERROR_ITERATOR_THROW)
|
|
JS_ThrowTypeError(ctx, "iterator does not have a throw method");
|
|
else
|
|
JS_ThrowInternalError(ctx, "invalid throw var type %d", type);
|
|
}
|
|
goto exception;
|
|
CASE(OP_eval):
|
|
{
|
|
JSValueConst obj;
|
|
int scope_idx;
|
|
call_argc = get_u16(pc);
|
|
scope_idx = get_u16(pc + 2) - 1;
|
|
pc += 4;
|
|
call_argv = sp - call_argc;
|
|
sf->cur_pc = pc;
|
|
if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) {
|
|
if (call_argc >= 1)
|
|
obj = call_argv[0];
|
|
else
|
|
obj = JS_UNDEFINED;
|
|
ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,
|
|
JS_EVAL_TYPE_DIRECT, scope_idx);
|
|
} else {
|
|
ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,
|
|
JS_UNDEFINED, call_argc, call_argv, 0);
|
|
}
|
|
if (UNLIKELY(JS_IsException(ret_val)))
|
|
goto exception;
|
|
for(i = -1; i < call_argc; i++)
|
|
JS_FreeValue(ctx, call_argv[i]);
|
|
sp -= call_argc + 1;
|
|
*sp++ = ret_val;
|
|
}
|
|
BREAK;
|
|
/* could merge with OP_apply */
|
|
CASE(OP_apply_eval):
|
|
{
|
|
int scope_idx;
|
|
uint32_t len;
|
|
JSValue *tab;
|
|
JSValueConst obj;
|
|
scope_idx = get_u16(pc) - 1;
|
|
pc += 2;
|
|
tab = build_arg_list(ctx, &len, sp[-1]);
|
|
if (!tab)
|
|
goto exception;
|
|
if (js_same_value(ctx, sp[-2], ctx->eval_obj)) {
|
|
if (len >= 1)
|
|
obj = tab[0];
|
|
else
|
|
obj = JS_UNDEFINED;
|
|
ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,
|
|
JS_EVAL_TYPE_DIRECT, scope_idx);
|
|
} else {
|
|
ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len,
|
|
(JSValueConst *)tab);
|
|
}
|
|
free_arg_list(ctx, tab, len);
|
|
if (UNLIKELY(JS_IsException(ret_val)))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-2]);
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp -= 2;
|
|
*sp++ = ret_val;
|
|
}
|
|
BREAK;
|
|
CASE(OP_regexp):
|
|
{
|
|
sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED,
|
|
sp[-2], sp[-1]);
|
|
sp--;
|
|
}
|
|
BREAK;
|
|
CASE(OP_get_super):
|
|
{
|
|
JSValue proto;
|
|
proto = JS_GetPrototype(ctx, sp[-1]);
|
|
if (JS_IsException(proto))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp[-1] = proto;
|
|
}
|
|
BREAK;
|
|
CASE(OP_import):
|
|
{
|
|
JSValue val;
|
|
val = js_dynamic_import(ctx, sp[-1]);
|
|
if (JS_IsException(val))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp[-1] = val;
|
|
}
|
|
BREAK;
|
|
CASE(OP_check_var):
|
|
{
|
|
int ret;
|
|
JSAtom atom;
|
|
atom = get_u32(pc);
|
|
pc += 4;
|
|
ret = JS_CheckGlobalVar(ctx, atom);
|
|
if (ret < 0)
|
|
goto exception;
|
|
*sp++ = JS_NewBool(ctx, ret);
|
|
}
|
|
BREAK;
|
|
CASE(OP_get_var_undef):
|
|
CASE(OP_get_var):
|
|
{
|
|
JSValue val;
|
|
JSAtom atom;
|
|
atom = get_u32(pc);
|
|
pc += 4;
|
|
val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef);
|
|
if (UNLIKELY(JS_IsException(val)))
|
|
goto exception;
|
|
*sp++ = val;
|
|
}
|
|
BREAK;
|
|
CASE(OP_put_var):
|
|
CASE(OP_put_var_init):
|
|
{
|
|
int ret;
|
|
JSAtom atom;
|
|
atom = get_u32(pc);
|
|
pc += 4;
|
|
ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var);
|
|
sp--;
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_put_var_strict):
|
|
{
|
|
int ret;
|
|
JSAtom atom;
|
|
atom = get_u32(pc);
|
|
pc += 4;
|
|
/* sp[-2] is JS_TRUE or JS_FALSE */
|
|
if (UNLIKELY(!JS_VALUE_GET_INT(sp[-2]))) {
|
|
JS_ThrowReferenceErrorNotDefined(ctx, atom);
|
|
goto exception;
|
|
}
|
|
ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2);
|
|
sp -= 2;
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_check_define_var):
|
|
{
|
|
JSAtom atom;
|
|
int flags;
|
|
atom = get_u32(pc);
|
|
flags = pc[4];
|
|
pc += 5;
|
|
if (JS_CheckDefineGlobalVar(ctx, atom, flags))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_define_var):
|
|
{
|
|
JSAtom atom;
|
|
int flags;
|
|
atom = get_u32(pc);
|
|
flags = pc[4];
|
|
pc += 5;
|
|
if (JS_DefineGlobalVar(ctx, atom, flags))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_define_func):
|
|
{
|
|
JSAtom atom;
|
|
int flags;
|
|
atom = get_u32(pc);
|
|
flags = pc[4];
|
|
pc += 5;
|
|
if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp--;
|
|
}
|
|
BREAK;
|
|
CASE(OP_get_loc):
|
|
{
|
|
int idx;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
sp[0] = JS_DupValue(ctx, var_buf[idx]);
|
|
sp++;
|
|
}
|
|
BREAK;
|
|
CASE(OP_put_loc):
|
|
{
|
|
int idx;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
set_value(ctx, &var_buf[idx], sp[-1]);
|
|
sp--;
|
|
}
|
|
BREAK;
|
|
CASE(OP_set_loc):
|
|
{
|
|
int idx;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1]));
|
|
}
|
|
BREAK;
|
|
CASE(OP_get_arg):
|
|
{
|
|
int idx;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
sp[0] = JS_DupValue(ctx, arg_buf[idx]);
|
|
sp++;
|
|
}
|
|
BREAK;
|
|
CASE(OP_put_arg):
|
|
{
|
|
int idx;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
set_value(ctx, &arg_buf[idx], sp[-1]);
|
|
sp--;
|
|
}
|
|
BREAK;
|
|
CASE(OP_set_arg):
|
|
{
|
|
int idx;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1]));
|
|
}
|
|
BREAK;
|
|
#if SHORT_OPCODES
|
|
CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK;
|
|
CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK;
|
|
CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK;
|
|
CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK;
|
|
CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK;
|
|
CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK;
|
|
CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK;
|
|
CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK;
|
|
CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK;
|
|
CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK;
|
|
CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK;
|
|
CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;
|
|
CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;
|
|
CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;
|
|
CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;
|
|
CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK;
|
|
CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK;
|
|
CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK;
|
|
CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK;
|
|
CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK;
|
|
CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK;
|
|
CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK;
|
|
CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK;
|
|
CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;
|
|
CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;
|
|
CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;
|
|
CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;
|
|
CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK;
|
|
CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK;
|
|
CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK;
|
|
CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK;
|
|
CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK;
|
|
CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK;
|
|
CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK;
|
|
CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK;
|
|
CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
|
|
CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
|
|
CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
|
|
CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
|
|
#endif
|
|
CASE(OP_get_var_ref):
|
|
{
|
|
int idx;
|
|
JSValue val;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
val = *var_refs[idx]->pvalue;
|
|
sp[0] = JS_DupValue(ctx, val);
|
|
sp++;
|
|
}
|
|
BREAK;
|
|
CASE(OP_put_var_ref):
|
|
{
|
|
int idx;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
|
|
sp--;
|
|
}
|
|
BREAK;
|
|
CASE(OP_set_var_ref):
|
|
{
|
|
int idx;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1]));
|
|
}
|
|
BREAK;
|
|
CASE(OP_get_var_ref_check):
|
|
{
|
|
int idx;
|
|
JSValue val;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
val = *var_refs[idx]->pvalue;
|
|
if (UNLIKELY(JS_IsUninitialized(val))) {
|
|
JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
|
|
goto exception;
|
|
}
|
|
sp[0] = JS_DupValue(ctx, val);
|
|
sp++;
|
|
}
|
|
BREAK;
|
|
CASE(OP_put_var_ref_check):
|
|
{
|
|
int idx;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
if (UNLIKELY(JS_IsUninitialized(*var_refs[idx]->pvalue))) {
|
|
JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
|
|
goto exception;
|
|
}
|
|
set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
|
|
sp--;
|
|
}
|
|
BREAK;
|
|
CASE(OP_put_var_ref_check_init):
|
|
{
|
|
int idx;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
if (UNLIKELY(!JS_IsUninitialized(*var_refs[idx]->pvalue))) {
|
|
JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
|
|
goto exception;
|
|
}
|
|
set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
|
|
sp--;
|
|
}
|
|
BREAK;
|
|
CASE(OP_set_loc_uninitialized):
|
|
{
|
|
int idx;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
set_value(ctx, &var_buf[idx], JS_UNINITIALIZED);
|
|
}
|
|
BREAK;
|
|
CASE(OP_get_loc_check):
|
|
{
|
|
int idx;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
if (UNLIKELY(JS_IsUninitialized(var_buf[idx]))) {
|
|
JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
|
|
goto exception;
|
|
}
|
|
sp[0] = JS_DupValue(ctx, var_buf[idx]);
|
|
sp++;
|
|
}
|
|
BREAK;
|
|
CASE(OP_put_loc_check):
|
|
{
|
|
int idx;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
if (UNLIKELY(JS_IsUninitialized(var_buf[idx]))) {
|
|
JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
|
|
goto exception;
|
|
}
|
|
set_value(ctx, &var_buf[idx], sp[-1]);
|
|
sp--;
|
|
}
|
|
BREAK;
|
|
CASE(OP_put_loc_check_init):
|
|
{
|
|
int idx;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
if (UNLIKELY(!JS_IsUninitialized(var_buf[idx]))) {
|
|
JS_ThrowReferenceError(ctx, "'this' can be initialized only once");
|
|
goto exception;
|
|
}
|
|
set_value(ctx, &var_buf[idx], sp[-1]);
|
|
sp--;
|
|
}
|
|
BREAK;
|
|
CASE(OP_close_loc):
|
|
{
|
|
int idx;
|
|
idx = get_u16(pc);
|
|
pc += 2;
|
|
close_lexical_var(ctx, sf, idx, FALSE);
|
|
}
|
|
BREAK;
|
|
CASE(OP_make_loc_ref):
|
|
CASE(OP_make_arg_ref):
|
|
CASE(OP_make_var_ref_ref):
|
|
{
|
|
JSVarRef *var_ref;
|
|
JSProperty *pr;
|
|
JSAtom atom;
|
|
int idx;
|
|
atom = get_u32(pc);
|
|
idx = get_u16(pc + 4);
|
|
pc += 6;
|
|
*sp++ = JS_NewObjectProto(ctx, JS_NULL);
|
|
if (UNLIKELY(JS_IsException(sp[-1])))
|
|
goto exception;
|
|
if (opcode == OP_make_var_ref_ref) {
|
|
var_ref = var_refs[idx];
|
|
var_ref->header.ref_count++;
|
|
} else {
|
|
var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref);
|
|
if (!var_ref)
|
|
goto exception;
|
|
}
|
|
pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom,
|
|
JS_PROP_WRITABLE | JS_PROP_VARREF);
|
|
if (!pr) {
|
|
free_var_ref(rt, var_ref);
|
|
goto exception;
|
|
}
|
|
pr->u.var_ref = var_ref;
|
|
*sp++ = JS_AtomToValue(ctx, atom);
|
|
}
|
|
BREAK;
|
|
CASE(OP_make_var_ref):
|
|
{
|
|
JSAtom atom;
|
|
atom = get_u32(pc);
|
|
pc += 4;
|
|
if (JS_GetGlobalVarRef(ctx, atom, sp))
|
|
goto exception;
|
|
sp += 2;
|
|
}
|
|
BREAK;
|
|
CASE(OP_goto):
|
|
pc += (int32_t)get_u32(pc);
|
|
if (UNLIKELY(js_poll_interrupts(ctx)))
|
|
goto exception;
|
|
BREAK;
|
|
#if SHORT_OPCODES
|
|
CASE(OP_goto16):
|
|
pc += (int16_t)get_u16(pc);
|
|
if (UNLIKELY(js_poll_interrupts(ctx)))
|
|
goto exception;
|
|
BREAK;
|
|
CASE(OP_goto8):
|
|
pc += (int8_t)pc[0];
|
|
if (UNLIKELY(js_poll_interrupts(ctx)))
|
|
goto exception;
|
|
BREAK;
|
|
#endif
|
|
CASE(OP_if_true):
|
|
{
|
|
int res;
|
|
JSValue op1;
|
|
op1 = sp[-1];
|
|
pc += 4;
|
|
if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
|
|
res = JS_VALUE_GET_INT(op1);
|
|
} else {
|
|
res = JS_ToBoolFree(ctx, op1);
|
|
}
|
|
sp--;
|
|
if (res) {
|
|
pc += (int32_t)get_u32(pc - 4) - 4;
|
|
}
|
|
if (UNLIKELY(js_poll_interrupts(ctx)))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_if_false):
|
|
{
|
|
int res;
|
|
JSValue op1;
|
|
op1 = sp[-1];
|
|
pc += 4;
|
|
if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
|
|
res = JS_VALUE_GET_INT(op1);
|
|
} else {
|
|
res = JS_ToBoolFree(ctx, op1);
|
|
}
|
|
sp--;
|
|
if (!res) {
|
|
pc += (int32_t)get_u32(pc - 4) - 4;
|
|
}
|
|
if (UNLIKELY(js_poll_interrupts(ctx)))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
#if SHORT_OPCODES
|
|
CASE(OP_if_true8):
|
|
{
|
|
int res;
|
|
JSValue op1;
|
|
op1 = sp[-1];
|
|
pc += 1;
|
|
if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
|
|
res = JS_VALUE_GET_INT(op1);
|
|
} else {
|
|
res = JS_ToBoolFree(ctx, op1);
|
|
}
|
|
sp--;
|
|
if (res) {
|
|
pc += (int8_t)pc[-1] - 1;
|
|
}
|
|
if (UNLIKELY(js_poll_interrupts(ctx)))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_if_false8):
|
|
{
|
|
int res;
|
|
JSValue op1;
|
|
op1 = sp[-1];
|
|
pc += 1;
|
|
if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
|
|
res = JS_VALUE_GET_INT(op1);
|
|
} else {
|
|
res = JS_ToBoolFree(ctx, op1);
|
|
}
|
|
sp--;
|
|
if (!res) {
|
|
pc += (int8_t)pc[-1] - 1;
|
|
}
|
|
if (UNLIKELY(js_poll_interrupts(ctx)))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
#endif
|
|
CASE(OP_catch):
|
|
{
|
|
int32_t diff;
|
|
diff = get_u32(pc);
|
|
sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf);
|
|
sp++;
|
|
pc += 4;
|
|
}
|
|
BREAK;
|
|
CASE(OP_gosub):
|
|
{
|
|
int32_t diff;
|
|
diff = get_u32(pc);
|
|
/* XXX: should have a different tag to avoid security flaw */
|
|
sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf);
|
|
sp++;
|
|
pc += diff;
|
|
}
|
|
BREAK;
|
|
CASE(OP_ret):
|
|
{
|
|
JSValue op1;
|
|
uint32_t pos;
|
|
op1 = sp[-1];
|
|
if (UNLIKELY(JS_VALUE_GET_TAG(op1) != JS_TAG_INT))
|
|
goto ret_fail;
|
|
pos = JS_VALUE_GET_INT(op1);
|
|
if (UNLIKELY(pos >= b->byte_code_len)) {
|
|
ret_fail:
|
|
JS_ThrowInternalError(ctx, "invalid ret value");
|
|
goto exception;
|
|
}
|
|
sp--;
|
|
pc = b->byte_code_buf + pos;
|
|
}
|
|
BREAK;
|
|
CASE(OP_for_in_start):
|
|
if (js_for_in_start(ctx, sp))
|
|
goto exception;
|
|
BREAK;
|
|
CASE(OP_for_in_next):
|
|
if (js_for_in_next(ctx, sp))
|
|
goto exception;
|
|
sp += 2;
|
|
BREAK;
|
|
CASE(OP_for_of_start):
|
|
if (js_for_of_start(ctx, sp, FALSE))
|
|
goto exception;
|
|
sp += 1;
|
|
*sp++ = JS_NewCatchOffset(ctx, 0);
|
|
BREAK;
|
|
CASE(OP_for_of_next):
|
|
{
|
|
int offset = -3 - pc[0];
|
|
pc += 1;
|
|
if (js_for_of_next(ctx, sp, offset))
|
|
goto exception;
|
|
sp += 2;
|
|
}
|
|
BREAK;
|
|
CASE(OP_for_await_of_start):
|
|
if (js_for_of_start(ctx, sp, TRUE))
|
|
goto exception;
|
|
sp += 1;
|
|
*sp++ = JS_NewCatchOffset(ctx, 0);
|
|
BREAK;
|
|
CASE(OP_iterator_get_value_done):
|
|
if (js_iterator_get_value_done(ctx, sp))
|
|
goto exception;
|
|
sp += 1;
|
|
BREAK;
|
|
CASE(OP_iterator_check_object):
|
|
if (UNLIKELY(!JS_IsObject(sp[-1]))) {
|
|
JS_ThrowTypeError(ctx, "iterator must return an object");
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_iterator_close):
|
|
/* iter_obj next catch_offset -> */
|
|
sp--; /* drop the catch offset to avoid getting caught by exception */
|
|
JS_FreeValue(ctx, sp[-1]); /* drop the next method */
|
|
sp--;
|
|
if (!JS_IsUndefined(sp[-1])) {
|
|
if (JS_IteratorClose(ctx, sp[-1], FALSE))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
}
|
|
sp--;
|
|
BREAK;
|
|
CASE(OP_iterator_close_return):
|
|
{
|
|
JSValue ret_val;
|
|
/* iter_obj next catch_offset ... ret_val ->
|
|
ret_eval iter_obj next catch_offset */
|
|
ret_val = *--sp;
|
|
while (sp > stack_buf &&
|
|
JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) {
|
|
JS_FreeValue(ctx, *--sp);
|
|
}
|
|
if (UNLIKELY(sp < stack_buf + 3)) {
|
|
JS_ThrowInternalError(ctx, "iterator_close_return");
|
|
JS_FreeValue(ctx, ret_val);
|
|
goto exception;
|
|
}
|
|
sp[0] = sp[-1];
|
|
sp[-1] = sp[-2];
|
|
sp[-2] = sp[-3];
|
|
sp[-3] = ret_val;
|
|
sp++;
|
|
}
|
|
BREAK;
|
|
CASE(OP_iterator_next):
|
|
/* stack: iter_obj next catch_offset val */
|
|
{
|
|
JSValue ret;
|
|
ret = JS_Call(ctx, sp[-3], sp[-4],
|
|
1, (JSValueConst *)(sp - 1));
|
|
if (JS_IsException(ret))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp[-1] = ret;
|
|
}
|
|
BREAK;
|
|
CASE(OP_iterator_call):
|
|
/* stack: iter_obj next catch_offset val */
|
|
{
|
|
JSValue method, ret;
|
|
BOOL ret_flag;
|
|
int flags;
|
|
flags = *pc++;
|
|
method = JS_GetProperty(ctx, sp[-4], (flags & 1) ?
|
|
JS_ATOM_throw : JS_ATOM_return);
|
|
if (JS_IsException(method))
|
|
goto exception;
|
|
if (JS_IsUndefined(method) || JS_IsNull(method)) {
|
|
ret_flag = TRUE;
|
|
} else {
|
|
if (flags & 2) {
|
|
/* no argument */
|
|
ret = JS_CallFree(ctx, method, sp[-4],
|
|
0, NULL);
|
|
} else {
|
|
ret = JS_CallFree(ctx, method, sp[-4],
|
|
1, (JSValueConst *)(sp - 1));
|
|
}
|
|
if (JS_IsException(ret))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp[-1] = ret;
|
|
ret_flag = FALSE;
|
|
}
|
|
sp[0] = JS_NewBool(ctx, ret_flag);
|
|
sp += 1;
|
|
}
|
|
BREAK;
|
|
CASE(OP_lnot):
|
|
{
|
|
int res;
|
|
JSValue op1;
|
|
op1 = sp[-1];
|
|
if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
|
|
res = JS_VALUE_GET_INT(op1) != 0;
|
|
} else {
|
|
res = JS_ToBoolFree(ctx, op1);
|
|
}
|
|
sp[-1] = JS_NewBool(ctx, !res);
|
|
}
|
|
BREAK;
|
|
CASE(OP_get_field):
|
|
{
|
|
JSValue val;
|
|
JSAtom atom;
|
|
atom = get_u32(pc);
|
|
pc += 4;
|
|
val = JS_GetProperty(ctx, sp[-1], atom);
|
|
if (UNLIKELY(JS_IsException(val)))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp[-1] = val;
|
|
}
|
|
BREAK;
|
|
CASE(OP_get_field2):
|
|
{
|
|
JSValue val;
|
|
JSAtom atom;
|
|
atom = get_u32(pc);
|
|
pc += 4;
|
|
val = JS_GetProperty(ctx, sp[-1], atom);
|
|
if (UNLIKELY(JS_IsException(val)))
|
|
goto exception;
|
|
*sp++ = val;
|
|
}
|
|
BREAK;
|
|
CASE(OP_put_field):
|
|
{
|
|
int ret;
|
|
JSAtom atom;
|
|
atom = get_u32(pc);
|
|
pc += 4;
|
|
ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1],
|
|
JS_PROP_THROW_STRICT);
|
|
JS_FreeValue(ctx, sp[-2]);
|
|
sp -= 2;
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_private_symbol):
|
|
{
|
|
JSAtom atom;
|
|
JSValue val;
|
|
atom = get_u32(pc);
|
|
pc += 4;
|
|
val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE);
|
|
if (JS_IsException(val))
|
|
goto exception;
|
|
*sp++ = val;
|
|
}
|
|
BREAK;
|
|
CASE(OP_get_private_field):
|
|
{
|
|
JSValue val;
|
|
val = JS_GetPrivateField(ctx, sp[-2], sp[-1]);
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
JS_FreeValue(ctx, sp[-2]);
|
|
sp[-2] = val;
|
|
sp--;
|
|
if (UNLIKELY(JS_IsException(val)))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_put_private_field):
|
|
{
|
|
int ret;
|
|
ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]);
|
|
JS_FreeValue(ctx, sp[-3]);
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp -= 3;
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_define_private_field):
|
|
{
|
|
int ret;
|
|
ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]);
|
|
JS_FreeValue(ctx, sp[-2]);
|
|
sp -= 2;
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_define_field):
|
|
{
|
|
int ret;
|
|
JSAtom atom;
|
|
atom = get_u32(pc);
|
|
pc += 4;
|
|
ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1],
|
|
JS_PROP_C_W_E | JS_PROP_THROW);
|
|
sp--;
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_set_name):
|
|
{
|
|
int ret;
|
|
JSAtom atom;
|
|
atom = get_u32(pc);
|
|
pc += 4;
|
|
ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE);
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_set_name_computed):
|
|
{
|
|
int ret;
|
|
ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE);
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_set_proto):
|
|
{
|
|
JSValue proto;
|
|
proto = sp[-1];
|
|
if (JS_IsObject(proto) || JS_IsNull(proto)) {
|
|
if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0)
|
|
goto exception;
|
|
}
|
|
JS_FreeValue(ctx, proto);
|
|
sp--;
|
|
}
|
|
BREAK;
|
|
CASE(OP_set_home_object):
|
|
js_method_set_home_object(ctx, sp[-1], sp[-2]);
|
|
BREAK;
|
|
CASE(OP_define_method):
|
|
CASE(OP_define_method_computed):
|
|
{
|
|
JSValue getter, setter, value;
|
|
JSValueConst obj;
|
|
JSAtom atom;
|
|
int flags, ret, op_flags;
|
|
BOOL is_computed;
|
|
is_computed = (opcode == OP_define_method_computed);
|
|
if (is_computed) {
|
|
atom = JS_ValueToAtom(ctx, sp[-2]);
|
|
if (UNLIKELY(atom == JS_ATOM_NULL))
|
|
goto exception;
|
|
opcode += OP_define_method - OP_define_method_computed;
|
|
} else {
|
|
atom = get_u32(pc);
|
|
pc += 4;
|
|
}
|
|
op_flags = *pc++;
|
|
obj = sp[-2 - is_computed];
|
|
flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE |
|
|
JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW;
|
|
if (op_flags & OP_DEFINE_METHOD_ENUMERABLE)
|
|
flags |= JS_PROP_ENUMERABLE;
|
|
op_flags &= 3;
|
|
value = JS_UNDEFINED;
|
|
getter = JS_UNDEFINED;
|
|
setter = JS_UNDEFINED;
|
|
if (op_flags == OP_DEFINE_METHOD_METHOD) {
|
|
value = sp[-1];
|
|
flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE;
|
|
} else if (op_flags == OP_DEFINE_METHOD_GETTER) {
|
|
getter = sp[-1];
|
|
flags |= JS_PROP_HAS_GET;
|
|
} else {
|
|
setter = sp[-1];
|
|
flags |= JS_PROP_HAS_SET;
|
|
}
|
|
ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj);
|
|
if (ret >= 0) {
|
|
ret = JS_DefineProperty(ctx, obj, atom, value,
|
|
getter, setter, flags);
|
|
}
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
if (is_computed) {
|
|
JS_FreeAtom(ctx, atom);
|
|
JS_FreeValue(ctx, sp[-2]);
|
|
}
|
|
sp -= 1 + is_computed;
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_define_class):
|
|
CASE(OP_define_class_computed):
|
|
{
|
|
int class_flags;
|
|
JSAtom atom;
|
|
atom = get_u32(pc);
|
|
class_flags = pc[4];
|
|
pc += 5;
|
|
if (js_op_define_class(ctx, sp, atom, class_flags,
|
|
var_refs, sf,
|
|
(opcode == OP_define_class_computed)) < 0)
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_get_array_el):
|
|
{
|
|
JSValue val;
|
|
val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
|
|
JS_FreeValue(ctx, sp[-2]);
|
|
sp[-2] = val;
|
|
sp--;
|
|
if (UNLIKELY(JS_IsException(val)))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_get_array_el2):
|
|
{
|
|
JSValue val;
|
|
val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
|
|
sp[-1] = val;
|
|
if (UNLIKELY(JS_IsException(val)))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_get_ref_value):
|
|
{
|
|
JSValue val;
|
|
if (UNLIKELY(JS_IsUndefined(sp[-2]))) {
|
|
JSAtom atom = JS_ValueToAtom(ctx, sp[-1]);
|
|
if (atom != JS_ATOM_NULL) {
|
|
JS_ThrowReferenceErrorNotDefined(ctx, atom);
|
|
JS_FreeAtom(ctx, atom);
|
|
}
|
|
goto exception;
|
|
}
|
|
val = JS_GetPropertyValue(ctx, sp[-2],
|
|
JS_DupValue(ctx, sp[-1]));
|
|
if (UNLIKELY(JS_IsException(val)))
|
|
goto exception;
|
|
sp[0] = val;
|
|
sp++;
|
|
}
|
|
BREAK;
|
|
CASE(OP_get_super_value):
|
|
{
|
|
JSValue val;
|
|
JSAtom atom;
|
|
atom = JS_ValueToAtom(ctx, sp[-1]);
|
|
if (UNLIKELY(atom == JS_ATOM_NULL))
|
|
goto exception;
|
|
val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE);
|
|
JS_FreeAtom(ctx, atom);
|
|
if (UNLIKELY(JS_IsException(val)))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
JS_FreeValue(ctx, sp[-2]);
|
|
JS_FreeValue(ctx, sp[-3]);
|
|
sp[-3] = val;
|
|
sp -= 2;
|
|
}
|
|
BREAK;
|
|
CASE(OP_put_array_el):
|
|
{
|
|
int ret;
|
|
ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT);
|
|
JS_FreeValue(ctx, sp[-3]);
|
|
sp -= 3;
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_put_ref_value):
|
|
{
|
|
int ret, flags;
|
|
flags = JS_PROP_THROW_STRICT;
|
|
if (UNLIKELY(JS_IsUndefined(sp[-3]))) {
|
|
if (is_strict_mode(ctx)) {
|
|
JSAtom atom = JS_ValueToAtom(ctx, sp[-2]);
|
|
if (atom != JS_ATOM_NULL) {
|
|
JS_ThrowReferenceErrorNotDefined(ctx, atom);
|
|
JS_FreeAtom(ctx, atom);
|
|
}
|
|
goto exception;
|
|
} else {
|
|
sp[-3] = JS_DupValue(ctx, ctx->global_obj);
|
|
}
|
|
} else {
|
|
if (is_strict_mode(ctx))
|
|
flags |= JS_PROP_NO_ADD;
|
|
}
|
|
ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags);
|
|
JS_FreeValue(ctx, sp[-3]);
|
|
sp -= 3;
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_put_super_value):
|
|
{
|
|
int ret;
|
|
JSAtom atom;
|
|
if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) {
|
|
JS_ThrowTypeErrorNotAnObject(ctx);
|
|
goto exception;
|
|
}
|
|
atom = JS_ValueToAtom(ctx, sp[-2]);
|
|
if (UNLIKELY(atom == JS_ATOM_NULL))
|
|
goto exception;
|
|
ret = JS_SetPropertyGeneric(ctx, sp[-3], atom, sp[-1], sp[-4],
|
|
JS_PROP_THROW_STRICT);
|
|
JS_FreeAtom(ctx, atom);
|
|
JS_FreeValue(ctx, sp[-4]);
|
|
JS_FreeValue(ctx, sp[-3]);
|
|
JS_FreeValue(ctx, sp[-2]);
|
|
sp -= 4;
|
|
if (ret < 0)
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_define_array_el):
|
|
{
|
|
int ret;
|
|
ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1],
|
|
JS_PROP_C_W_E | JS_PROP_THROW);
|
|
sp -= 1;
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_append): /* array pos enumobj -- array pos */
|
|
{
|
|
if (js_append_enumerate(ctx, sp))
|
|
goto exception;
|
|
JS_FreeValue(ctx, *--sp);
|
|
}
|
|
BREAK;
|
|
CASE(OP_copy_data_properties): /* target source excludeList */
|
|
{
|
|
/* stack offsets (-1 based):
|
|
2 bits for target,
|
|
3 bits for source,
|
|
2 bits for exclusionList */
|
|
int mask;
|
|
mask = *pc++;
|
|
if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)],
|
|
sp[-1 - ((mask >> 2) & 7)],
|
|
sp[-1 - ((mask >> 5) & 7)], 0))
|
|
goto exception;
|
|
}
|
|
BREAK;
|
|
CASE(OP_add):
|
|
{
|
|
JSValue op1, op2;
|
|
op1 = sp[-2];
|
|
op2 = sp[-1];
|
|
if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) {
|
|
int64_t r;
|
|
r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2);
|
|
if (UNLIKELY((int)r != r))
|
|
goto add_slow;
|
|
sp[-2] = JS_NewInt32(ctx, r);
|
|
sp--;
|
|
} else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
|
|
sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) +
|
|
JS_VALUE_GET_FLOAT64(op2));
|
|
sp--;
|
|
} else {
|
|
add_slow:
|
|
if (js_add_slow(ctx, sp))
|
|
goto exception;
|
|
sp--;
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_add_loc):
|
|
{
|
|
JSValue *pv;
|
|
int idx;
|
|
idx = *pc;
|
|
pc += 1;
|
|
pv = &var_buf[idx];
|
|
if (LIKELY(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) {
|
|
int64_t r;
|
|
r = (int64_t)JS_VALUE_GET_INT(*pv) +
|
|
JS_VALUE_GET_INT(sp[-1]);
|
|
if (UNLIKELY((int)r != r))
|
|
goto add_loc_slow;
|
|
*pv = JS_NewInt32(ctx, r);
|
|
sp--;
|
|
} else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) {
|
|
JSValue op1;
|
|
op1 = sp[-1];
|
|
sp--;
|
|
op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
|
|
if (JS_IsException(op1))
|
|
goto exception;
|
|
op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1);
|
|
if (JS_IsException(op1))
|
|
goto exception;
|
|
set_value(ctx, pv, op1);
|
|
} else {
|
|
JSValue ops[2];
|
|
add_loc_slow:
|
|
/* In case of exception, js_add_slow frees ops[0]
|
|
and ops[1], so we must duplicate *pv */
|
|
ops[0] = JS_DupValue(ctx, *pv);
|
|
ops[1] = sp[-1];
|
|
sp--;
|
|
if (js_add_slow(ctx, ops + 2))
|
|
goto exception;
|
|
set_value(ctx, pv, ops[0]);
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_sub):
|
|
{
|
|
JSValue op1, op2;
|
|
op1 = sp[-2];
|
|
op2 = sp[-1];
|
|
if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) {
|
|
int64_t r;
|
|
r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2);
|
|
if (UNLIKELY((int)r != r))
|
|
goto binary_arith_slow;
|
|
sp[-2] = JS_NewInt32(ctx, r);
|
|
sp--;
|
|
} else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
|
|
sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) -
|
|
JS_VALUE_GET_FLOAT64(op2));
|
|
sp--;
|
|
} else {
|
|
goto binary_arith_slow;
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_mul):
|
|
{
|
|
JSValue op1, op2;
|
|
double d;
|
|
op1 = sp[-2];
|
|
op2 = sp[-1];
|
|
if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) {
|
|
int32_t v1, v2;
|
|
int64_t r;
|
|
v1 = JS_VALUE_GET_INT(op1);
|
|
v2 = JS_VALUE_GET_INT(op2);
|
|
r = (int64_t)v1 * v2;
|
|
if (UNLIKELY((int)r != r)) {
|
|
#ifdef CONFIG_BIGNUM
|
|
if (UNLIKELY(sf->js_mode & JS_MODE_MATH) &&
|
|
(r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER))
|
|
goto binary_arith_slow;
|
|
#endif
|
|
d = (double)r;
|
|
goto mul_fp_res;
|
|
}
|
|
/* need to test zero case for -0 result */
|
|
if (UNLIKELY(r == 0 && (v1 | v2) < 0)) {
|
|
d = -0.0;
|
|
goto mul_fp_res;
|
|
}
|
|
sp[-2] = JS_NewInt32(ctx, r);
|
|
sp--;
|
|
} else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
|
|
#ifdef CONFIG_BIGNUM
|
|
if (UNLIKELY(sf->js_mode & JS_MODE_MATH))
|
|
goto binary_arith_slow;
|
|
#endif
|
|
d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2);
|
|
mul_fp_res:
|
|
sp[-2] = __JS_NewFloat64(ctx, d);
|
|
sp--;
|
|
} else {
|
|
goto binary_arith_slow;
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_div):
|
|
{
|
|
JSValue op1, op2;
|
|
op1 = sp[-2];
|
|
op2 = sp[-1];
|
|
if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) {
|
|
int v1, v2;
|
|
if (UNLIKELY(sf->js_mode & JS_MODE_MATH))
|
|
goto binary_arith_slow;
|
|
v1 = JS_VALUE_GET_INT(op1);
|
|
v2 = JS_VALUE_GET_INT(op2);
|
|
sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);
|
|
sp--;
|
|
} else {
|
|
goto binary_arith_slow;
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_mod):
|
|
#ifdef CONFIG_BIGNUM
|
|
CASE(OP_math_mod):
|
|
#endif
|
|
{
|
|
JSValue op1, op2;
|
|
op1 = sp[-2];
|
|
op2 = sp[-1];
|
|
if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) {
|
|
int v1, v2, r;
|
|
v1 = JS_VALUE_GET_INT(op1);
|
|
v2 = JS_VALUE_GET_INT(op2);
|
|
/* We must avoid v2 = 0, v1 = INT32_MIN and v2 =
|
|
-1 and the cases where the result is -0. */
|
|
if (UNLIKELY(v1 < 0 || v2 <= 0))
|
|
goto binary_arith_slow;
|
|
r = v1 % v2;
|
|
sp[-2] = JS_NewInt32(ctx, r);
|
|
sp--;
|
|
} else {
|
|
goto binary_arith_slow;
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_pow):
|
|
binary_arith_slow:
|
|
if (js_binary_arith_slow(ctx, sp, opcode))
|
|
goto exception;
|
|
sp--;
|
|
BREAK;
|
|
CASE(OP_plus):
|
|
{
|
|
JSValue op1;
|
|
uint32_t tag;
|
|
op1 = sp[-1];
|
|
tag = JS_VALUE_GET_TAG(op1);
|
|
if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) {
|
|
} else {
|
|
if (js_unary_arith_slow(ctx, sp, opcode))
|
|
goto exception;
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_neg):
|
|
{
|
|
JSValue op1;
|
|
uint32_t tag;
|
|
int val;
|
|
double d;
|
|
op1 = sp[-1];
|
|
tag = JS_VALUE_GET_TAG(op1);
|
|
if (tag == JS_TAG_INT) {
|
|
val = JS_VALUE_GET_INT(op1);
|
|
/* Note: -0 cannot be expressed as integer */
|
|
if (UNLIKELY(val == 0)) {
|
|
d = -0.0;
|
|
goto neg_fp_res;
|
|
}
|
|
if (UNLIKELY(val == INT32_MIN)) {
|
|
d = -(double)val;
|
|
goto neg_fp_res;
|
|
}
|
|
sp[-1] = JS_NewInt32(ctx, -val);
|
|
} else if (JS_TAG_IS_FLOAT64(tag)) {
|
|
d = -JS_VALUE_GET_FLOAT64(op1);
|
|
neg_fp_res:
|
|
sp[-1] = __JS_NewFloat64(ctx, d);
|
|
} else {
|
|
if (js_unary_arith_slow(ctx, sp, opcode))
|
|
goto exception;
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_inc):
|
|
{
|
|
JSValue op1;
|
|
int val;
|
|
op1 = sp[-1];
|
|
if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
|
|
val = JS_VALUE_GET_INT(op1);
|
|
if (UNLIKELY(val == INT32_MAX))
|
|
goto inc_slow;
|
|
sp[-1] = JS_NewInt32(ctx, val + 1);
|
|
} else {
|
|
inc_slow:
|
|
if (js_unary_arith_slow(ctx, sp, opcode))
|
|
goto exception;
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_dec):
|
|
{
|
|
JSValue op1;
|
|
int val;
|
|
op1 = sp[-1];
|
|
if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
|
|
val = JS_VALUE_GET_INT(op1);
|
|
if (UNLIKELY(val == INT32_MIN))
|
|
goto dec_slow;
|
|
sp[-1] = JS_NewInt32(ctx, val - 1);
|
|
} else {
|
|
dec_slow:
|
|
if (js_unary_arith_slow(ctx, sp, opcode))
|
|
goto exception;
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_post_inc):
|
|
CASE(OP_post_dec):
|
|
if (js_post_inc_slow(ctx, sp, opcode))
|
|
goto exception;
|
|
sp++;
|
|
BREAK;
|
|
CASE(OP_inc_loc):
|
|
{
|
|
JSValue op1;
|
|
int val;
|
|
int idx;
|
|
idx = *pc;
|
|
pc += 1;
|
|
op1 = var_buf[idx];
|
|
if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
|
|
val = JS_VALUE_GET_INT(op1);
|
|
if (UNLIKELY(val == INT32_MAX))
|
|
goto inc_loc_slow;
|
|
var_buf[idx] = JS_NewInt32(ctx, val + 1);
|
|
} else {
|
|
inc_loc_slow:
|
|
/* must duplicate otherwise the variable value may
|
|
be destroyed before JS code accesses it */
|
|
op1 = JS_DupValue(ctx, op1);
|
|
if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc))
|
|
goto exception;
|
|
set_value(ctx, &var_buf[idx], op1);
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_dec_loc):
|
|
{
|
|
JSValue op1;
|
|
int val;
|
|
int idx;
|
|
idx = *pc;
|
|
pc += 1;
|
|
op1 = var_buf[idx];
|
|
if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
|
|
val = JS_VALUE_GET_INT(op1);
|
|
if (UNLIKELY(val == INT32_MIN))
|
|
goto dec_loc_slow;
|
|
var_buf[idx] = JS_NewInt32(ctx, val - 1);
|
|
} else {
|
|
dec_loc_slow:
|
|
/* must duplicate otherwise the variable value may
|
|
be destroyed before JS code accesses it */
|
|
op1 = JS_DupValue(ctx, op1);
|
|
if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec))
|
|
goto exception;
|
|
set_value(ctx, &var_buf[idx], op1);
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_not):
|
|
{
|
|
JSValue op1;
|
|
op1 = sp[-1];
|
|
if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
|
|
sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1));
|
|
} else {
|
|
if (js_not_slow(ctx, sp))
|
|
goto exception;
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_shl):
|
|
{
|
|
JSValue op1, op2;
|
|
op1 = sp[-2];
|
|
op2 = sp[-1];
|
|
if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) {
|
|
uint32_t v1, v2;
|
|
v1 = JS_VALUE_GET_INT(op1);
|
|
v2 = JS_VALUE_GET_INT(op2);
|
|
#ifdef CONFIG_BIGNUM
|
|
{
|
|
int64_t r;
|
|
if (UNLIKELY(sf->js_mode & JS_MODE_MATH)) {
|
|
if (v2 > 0x1f)
|
|
goto shl_slow;
|
|
r = (int64_t)v1 << v2;
|
|
if ((int)r != r)
|
|
goto shl_slow;
|
|
} else {
|
|
v2 &= 0x1f;
|
|
}
|
|
}
|
|
#else
|
|
v2 &= 0x1f;
|
|
#endif
|
|
sp[-2] = JS_NewInt32(ctx, v1 << v2);
|
|
sp--;
|
|
} else {
|
|
#ifdef CONFIG_BIGNUM
|
|
shl_slow:
|
|
#endif
|
|
if (js_binary_logic_slow(ctx, sp, opcode))
|
|
goto exception;
|
|
sp--;
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_shr):
|
|
{
|
|
JSValue op1, op2;
|
|
op1 = sp[-2];
|
|
op2 = sp[-1];
|
|
if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) {
|
|
uint32_t v2;
|
|
v2 = JS_VALUE_GET_INT(op2);
|
|
/* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */
|
|
v2 &= 0x1f;
|
|
sp[-2] = JS_NewUint32(ctx,
|
|
(uint32_t)JS_VALUE_GET_INT(op1) >>
|
|
v2);
|
|
sp--;
|
|
} else {
|
|
if (js_shr_slow(ctx, sp))
|
|
goto exception;
|
|
sp--;
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_sar):
|
|
{
|
|
JSValue op1, op2;
|
|
op1 = sp[-2];
|
|
op2 = sp[-1];
|
|
if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) {
|
|
uint32_t v2;
|
|
v2 = JS_VALUE_GET_INT(op2);
|
|
#ifdef CONFIG_BIGNUM
|
|
if (UNLIKELY(v2 > 0x1f)) {
|
|
if (UNLIKELY(sf->js_mode & JS_MODE_MATH))
|
|
goto sar_slow;
|
|
else
|
|
v2 &= 0x1f;
|
|
}
|
|
#else
|
|
v2 &= 0x1f;
|
|
#endif
|
|
sp[-2] = JS_NewInt32(ctx,
|
|
(int)JS_VALUE_GET_INT(op1) >> v2);
|
|
sp--;
|
|
} else {
|
|
#ifdef CONFIG_BIGNUM
|
|
sar_slow:
|
|
#endif
|
|
if (js_binary_logic_slow(ctx, sp, opcode))
|
|
goto exception;
|
|
sp--;
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_and):
|
|
{
|
|
JSValue op1, op2;
|
|
op1 = sp[-2];
|
|
op2 = sp[-1];
|
|
if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) {
|
|
sp[-2] = JS_NewInt32(ctx,
|
|
JS_VALUE_GET_INT(op1) &
|
|
JS_VALUE_GET_INT(op2));
|
|
sp--;
|
|
} else {
|
|
if (js_binary_logic_slow(ctx, sp, opcode))
|
|
goto exception;
|
|
sp--;
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_or):
|
|
{
|
|
JSValue op1, op2;
|
|
op1 = sp[-2];
|
|
op2 = sp[-1];
|
|
if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) {
|
|
sp[-2] = JS_NewInt32(ctx,
|
|
JS_VALUE_GET_INT(op1) |
|
|
JS_VALUE_GET_INT(op2));
|
|
sp--;
|
|
} else {
|
|
if (js_binary_logic_slow(ctx, sp, opcode))
|
|
goto exception;
|
|
sp--;
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_xor):
|
|
{
|
|
JSValue op1, op2;
|
|
op1 = sp[-2];
|
|
op2 = sp[-1];
|
|
if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) {
|
|
sp[-2] = JS_NewInt32(ctx,
|
|
JS_VALUE_GET_INT(op1) ^
|
|
JS_VALUE_GET_INT(op2));
|
|
sp--;
|
|
} else {
|
|
if (js_binary_logic_slow(ctx, sp, opcode))
|
|
goto exception;
|
|
sp--;
|
|
}
|
|
}
|
|
BREAK;
|
|
#define OP_CMP(opcode, binary_op, slow_call) \
|
|
CASE(opcode): \
|
|
{ \
|
|
JSValue op1, op2; \
|
|
op1 = sp[-2]; \
|
|
op2 = sp[-1]; \
|
|
if (LIKELY(JS_VALUE_IS_BOTH_INT(op1, op2))) { \
|
|
sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \
|
|
sp--; \
|
|
} else { \
|
|
if (slow_call) \
|
|
goto exception; \
|
|
sp--; \
|
|
} \
|
|
} \
|
|
BREAK
|
|
OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode));
|
|
OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode));
|
|
OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode));
|
|
OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode));
|
|
OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0));
|
|
OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1));
|
|
OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0));
|
|
OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1));
|
|
#ifdef CONFIG_BIGNUM
|
|
CASE(OP_mul_pow10):
|
|
if (rt->bigfloat_ops.mul_pow10(ctx, sp))
|
|
goto exception;
|
|
sp--;
|
|
BREAK;
|
|
#endif
|
|
CASE(OP_in):
|
|
if (js_operator_in(ctx, sp))
|
|
goto exception;
|
|
sp--;
|
|
BREAK;
|
|
CASE(OP_instanceof):
|
|
if (js_operator_instanceof(ctx, sp))
|
|
goto exception;
|
|
sp--;
|
|
BREAK;
|
|
CASE(OP_typeof):
|
|
{
|
|
JSValue op1;
|
|
JSAtom atom;
|
|
op1 = sp[-1];
|
|
atom = js_operator_typeof(ctx, op1);
|
|
JS_FreeValue(ctx, op1);
|
|
sp[-1] = JS_AtomToString(ctx, atom);
|
|
}
|
|
BREAK;
|
|
CASE(OP_delete):
|
|
if (js_operator_delete(ctx, sp))
|
|
goto exception;
|
|
sp--;
|
|
BREAK;
|
|
CASE(OP_delete_var):
|
|
{
|
|
JSAtom atom;
|
|
int ret;
|
|
atom = get_u32(pc);
|
|
pc += 4;
|
|
ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0);
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
*sp++ = JS_NewBool(ctx, ret);
|
|
}
|
|
BREAK;
|
|
CASE(OP_to_object):
|
|
if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) {
|
|
ret_val = JS_ToObject(ctx, sp[-1]);
|
|
if (JS_IsException(ret_val))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp[-1] = ret_val;
|
|
}
|
|
BREAK;
|
|
CASE(OP_to_propkey):
|
|
switch (JS_VALUE_GET_TAG(sp[-1])) {
|
|
case JS_TAG_INT:
|
|
case JS_TAG_STRING:
|
|
case JS_TAG_SYMBOL:
|
|
break;
|
|
default:
|
|
ret_val = JS_ToPropertyKey(ctx, sp[-1]);
|
|
if (JS_IsException(ret_val))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp[-1] = ret_val;
|
|
break;
|
|
}
|
|
BREAK;
|
|
CASE(OP_to_propkey2):
|
|
/* must be tested first */
|
|
if (UNLIKELY(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) {
|
|
JS_ThrowTypeError(ctx, "value has no property");
|
|
goto exception;
|
|
}
|
|
switch (JS_VALUE_GET_TAG(sp[-1])) {
|
|
case JS_TAG_INT:
|
|
case JS_TAG_STRING:
|
|
case JS_TAG_SYMBOL:
|
|
break;
|
|
default:
|
|
ret_val = JS_ToPropertyKey(ctx, sp[-1]);
|
|
if (JS_IsException(ret_val))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp[-1] = ret_val;
|
|
break;
|
|
}
|
|
BREAK;
|
|
#if 0
|
|
CASE(OP_to_string):
|
|
if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) {
|
|
ret_val = JS_ToString(ctx, sp[-1]);
|
|
if (JS_IsException(ret_val))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp[-1] = ret_val;
|
|
}
|
|
BREAK;
|
|
#endif
|
|
CASE(OP_with_get_var):
|
|
CASE(OP_with_put_var):
|
|
CASE(OP_with_delete_var):
|
|
CASE(OP_with_make_ref):
|
|
CASE(OP_with_get_ref):
|
|
CASE(OP_with_get_ref_undef):
|
|
{
|
|
JSAtom atom;
|
|
int32_t diff;
|
|
JSValue obj, val;
|
|
int ret, is_with;
|
|
atom = get_u32(pc);
|
|
diff = get_u32(pc + 4);
|
|
is_with = pc[8];
|
|
pc += 9;
|
|
obj = sp[-1];
|
|
ret = JS_HasProperty(ctx, obj, atom);
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
if (ret) {
|
|
if (is_with) {
|
|
ret = js_has_unscopable(ctx, obj, atom);
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
if (ret)
|
|
goto no_with;
|
|
}
|
|
switch (opcode) {
|
|
case OP_with_get_var:
|
|
val = JS_GetProperty(ctx, obj, atom);
|
|
if (UNLIKELY(JS_IsException(val)))
|
|
goto exception;
|
|
set_value(ctx, &sp[-1], val);
|
|
break;
|
|
case OP_with_put_var:
|
|
/* XXX: check if strict mode */
|
|
ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2],
|
|
JS_PROP_THROW_STRICT);
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp -= 2;
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
break;
|
|
case OP_with_delete_var:
|
|
ret = JS_DeleteProperty(ctx, obj, atom, 0);
|
|
if (UNLIKELY(ret < 0))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp[-1] = JS_NewBool(ctx, ret);
|
|
break;
|
|
case OP_with_make_ref:
|
|
/* produce a pair object/propname on the stack */
|
|
*sp++ = JS_AtomToValue(ctx, atom);
|
|
break;
|
|
case OP_with_get_ref:
|
|
/* produce a pair object/method on the stack */
|
|
val = JS_GetProperty(ctx, obj, atom);
|
|
if (UNLIKELY(JS_IsException(val)))
|
|
goto exception;
|
|
*sp++ = val;
|
|
break;
|
|
case OP_with_get_ref_undef:
|
|
/* produce a pair undefined/function on the stack */
|
|
val = JS_GetProperty(ctx, obj, atom);
|
|
if (UNLIKELY(JS_IsException(val)))
|
|
goto exception;
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp[-1] = JS_UNDEFINED;
|
|
*sp++ = val;
|
|
break;
|
|
}
|
|
pc += diff - 5;
|
|
} else {
|
|
no_with:
|
|
/* if not jumping, drop the object argument */
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp--;
|
|
}
|
|
}
|
|
BREAK;
|
|
CASE(OP_await):
|
|
ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT);
|
|
goto done_generator;
|
|
CASE(OP_yield):
|
|
ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD);
|
|
goto done_generator;
|
|
CASE(OP_yield_star):
|
|
CASE(OP_async_yield_star):
|
|
ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR);
|
|
goto done_generator;
|
|
CASE(OP_return_async):
|
|
CASE(OP_initial_yield):
|
|
ret_val = JS_UNDEFINED;
|
|
goto done_generator;
|
|
CASE(OP_nop):
|
|
BREAK;
|
|
CASE(OP_is_undefined_or_null):
|
|
if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED ||
|
|
JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {
|
|
goto set_true;
|
|
} else {
|
|
goto free_and_set_false;
|
|
}
|
|
#if SHORT_OPCODES
|
|
CASE(OP_is_undefined):
|
|
if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) {
|
|
goto set_true;
|
|
} else {
|
|
goto free_and_set_false;
|
|
}
|
|
CASE(OP_is_null):
|
|
if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {
|
|
goto set_true;
|
|
} else {
|
|
goto free_and_set_false;
|
|
}
|
|
/* XXX: could merge to a single opcode */
|
|
CASE(OP_typeof_is_undefined):
|
|
/* different from OP_is_undefined because of isHTMLDDA */
|
|
if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) {
|
|
goto free_and_set_true;
|
|
} else {
|
|
goto free_and_set_false;
|
|
}
|
|
CASE(OP_typeof_is_function):
|
|
if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) {
|
|
goto free_and_set_true;
|
|
} else {
|
|
goto free_and_set_false;
|
|
}
|
|
free_and_set_true:
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
#endif
|
|
set_true:
|
|
sp[-1] = JS_TRUE;
|
|
BREAK;
|
|
free_and_set_false:
|
|
JS_FreeValue(ctx, sp[-1]);
|
|
sp[-1] = JS_FALSE;
|
|
BREAK;
|
|
CASE(OP_invalid):
|
|
DEFAULT:
|
|
JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x",
|
|
(int)(pc - b->byte_code_buf - 1), opcode);
|
|
goto exception;
|
|
}
|
|
}
|
|
exception:
|
|
if (is_backtrace_needed(ctx, rt->current_exception)) {
|
|
/* add the backtrace information now (it is not done
|
|
before if the exception happens in a bytecode
|
|
operation */
|
|
sf->cur_pc = pc;
|
|
build_backtrace(ctx, rt->current_exception, NULL, 0, 0);
|
|
}
|
|
if (!JS_IsUncatchableError(ctx, rt->current_exception)) {
|
|
while (sp > stack_buf) {
|
|
JSValue val = *--sp;
|
|
JS_FreeValue(ctx, val);
|
|
if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) {
|
|
int pos = JS_VALUE_GET_INT(val);
|
|
if (pos == 0) {
|
|
/* enumerator: close it with a throw */
|
|
JS_FreeValue(ctx, sp[-1]); /* drop the next method */
|
|
sp--;
|
|
JS_IteratorClose(ctx, sp[-1], TRUE);
|
|
} else {
|
|
*sp++ = rt->current_exception;
|
|
rt->current_exception = JS_NULL;
|
|
pc = b->byte_code_buf + pos;
|
|
goto restart;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ret_val = JS_EXCEPTION;
|
|
/* the local variables are freed by the caller in the generator
|
|
case. Hence the label 'done' should never be reached in a
|
|
generator function. */
|
|
if (b->func_kind != JS_FUNC_NORMAL) {
|
|
done_generator:
|
|
sf->cur_pc = pc;
|
|
sf->cur_sp = sp;
|
|
} else {
|
|
done:
|
|
if (UNLIKELY(!list_empty(&sf->var_ref_list))) {
|
|
/* variable references reference the stack: must close them */
|
|
close_var_refs(rt, sf);
|
|
}
|
|
/* free the local variables and stack */
|
|
for(pval = local_buf; pval < sp; pval++) {
|
|
JS_FreeValue(ctx, *pval);
|
|
}
|
|
}
|
|
rt->current_stack_frame = sf->prev_frame;
|
|
return ret_val;
|
|
}
|
|
|
|
JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
|
|
argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);
|
|
}
|
|
|
|
JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
|
|
argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);
|
|
JS_FreeValue(ctx, func_obj);
|
|
return res;
|
|
}
|
|
|
|
/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
|
|
static JSValue JS_CallConstructorInternal(JSContext *ctx,
|
|
JSValueConst func_obj,
|
|
JSValueConst new_target,
|
|
int argc, JSValue *argv, int flags)
|
|
{
|
|
JSObject *p;
|
|
JSFunctionBytecode *b;
|
|
if (js_poll_interrupts(ctx))
|
|
return JS_EXCEPTION;
|
|
flags |= JS_CALL_FLAG_CONSTRUCTOR;
|
|
if (UNLIKELY(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT))
|
|
goto not_a_function;
|
|
p = JS_VALUE_GET_OBJ(func_obj);
|
|
if (UNLIKELY(!p->is_constructor))
|
|
return JS_ThrowTypeError(ctx, "not a constructor");
|
|
if (UNLIKELY(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
|
|
JSClassCall *call_func;
|
|
call_func = ctx->rt->class_array[p->class_id].call;
|
|
if (!call_func) {
|
|
not_a_function:
|
|
return JS_ThrowTypeError(ctx, "not a function");
|
|
}
|
|
return call_func(ctx, func_obj, new_target, argc,
|
|
(JSValueConst *)argv, flags);
|
|
}
|
|
b = p->u.func.function_bytecode;
|
|
if (b->is_derived_class_constructor) {
|
|
return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags);
|
|
} else {
|
|
JSValue obj, ret;
|
|
/* legacy constructor behavior */
|
|
obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
|
|
if (JS_IsException(obj))
|
|
return JS_EXCEPTION;
|
|
ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags);
|
|
if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT ||
|
|
JS_IsException(ret)) {
|
|
JS_FreeValue(ctx, obj);
|
|
return ret;
|
|
} else {
|
|
JS_FreeValue(ctx, ret);
|
|
return obj;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* XXX: add a specific eval mode so that Function("}), ({") is rejected */
|
|
JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target,
|
|
int argc, JSValueConst *argv, int magic)
|
|
{
|
|
JSFunctionKindEnum func_kind = magic;
|
|
int i, n, ret;
|
|
JSValue s, proto, obj = JS_UNDEFINED;
|
|
StringBuffer b_s, *b = &b_s;
|
|
string_buffer_init(ctx, b, 0);
|
|
string_buffer_putc8(b, '(');
|
|
if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) {
|
|
string_buffer_puts8(b, "async ");
|
|
}
|
|
string_buffer_puts8(b, "function");
|
|
if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) {
|
|
string_buffer_putc8(b, '*');
|
|
}
|
|
string_buffer_puts8(b, " anonymous(");
|
|
n = argc - 1;
|
|
for(i = 0; i < n; i++) {
|
|
if (i != 0) {
|
|
string_buffer_putc8(b, ',');
|
|
}
|
|
if (string_buffer_concat_value(b, argv[i]))
|
|
goto fail;
|
|
}
|
|
string_buffer_puts8(b, "\n) {\n");
|
|
if (n >= 0) {
|
|
if (string_buffer_concat_value(b, argv[n]))
|
|
goto fail;
|
|
}
|
|
string_buffer_puts8(b, "\n})");
|
|
s = string_buffer_end(b);
|
|
if (JS_IsException(s))
|
|
goto fail1;
|
|
obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1);
|
|
JS_FreeValue(ctx, s);
|
|
if (JS_IsException(obj))
|
|
goto fail1;
|
|
if (!JS_IsUndefined(new_target)) {
|
|
/* set the prototype */
|
|
proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);
|
|
if (JS_IsException(proto))
|
|
goto fail1;
|
|
if (!JS_IsObject(proto)) {
|
|
JSContext *realm;
|
|
JS_FreeValue(ctx, proto);
|
|
realm = JS_GetFunctionRealm(ctx, new_target);
|
|
if (!realm)
|
|
goto fail1;
|
|
proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]);
|
|
}
|
|
ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE);
|
|
JS_FreeValue(ctx, proto);
|
|
if (ret < 0)
|
|
goto fail1;
|
|
}
|
|
return obj;
|
|
fail:
|
|
string_buffer_free(b);
|
|
fail1:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
/* JSAsyncFunctionState (used by generator and async functions) */
|
|
int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,
|
|
JSValueConst func_obj, JSValueConst this_obj,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSObject *p;
|
|
JSFunctionBytecode *b;
|
|
JSStackFrame *sf;
|
|
int local_count, i, arg_buf_len, n;
|
|
sf = &s->frame;
|
|
init_list_head(&sf->var_ref_list);
|
|
p = JS_VALUE_GET_OBJ(func_obj);
|
|
b = p->u.func.function_bytecode;
|
|
sf->js_mode = b->js_mode;
|
|
sf->cur_pc = b->byte_code_buf;
|
|
arg_buf_len = max_int(b->arg_count, argc);
|
|
local_count = arg_buf_len + b->var_count + b->stack_size;
|
|
sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1));
|
|
if (!sf->arg_buf)
|
|
return -1;
|
|
sf->cur_func = JS_DupValue(ctx, func_obj);
|
|
s->this_val = JS_DupValue(ctx, this_obj);
|
|
s->argc = argc;
|
|
sf->arg_count = arg_buf_len;
|
|
sf->var_buf = sf->arg_buf + arg_buf_len;
|
|
sf->cur_sp = sf->var_buf + b->var_count;
|
|
for(i = 0; i < argc; i++)
|
|
sf->arg_buf[i] = JS_DupValue(ctx, argv[i]);
|
|
n = arg_buf_len + b->var_count;
|
|
for(i = argc; i < n; i++)
|
|
sf->arg_buf[i] = JS_UNDEFINED;
|
|
return 0;
|
|
}
|
|
|
|
void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, JS_MarkFunc *mark_func)
|
|
{
|
|
JSStackFrame *sf;
|
|
JSValue *sp;
|
|
sf = &s->frame;
|
|
JS_MarkValue(rt, sf->cur_func, mark_func);
|
|
JS_MarkValue(rt, s->this_val, mark_func);
|
|
if (sf->cur_sp) {
|
|
/* if the function is running, cur_sp is not known so we
|
|
cannot mark the stack. Marking the variables is not needed
|
|
because a running function cannot be part of a removable
|
|
cycle */
|
|
for(sp = sf->arg_buf; sp < sf->cur_sp; sp++)
|
|
JS_MarkValue(rt, *sp, mark_func);
|
|
}
|
|
}
|
|
|
|
void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
|
|
{
|
|
JSStackFrame *sf;
|
|
JSValue *sp;
|
|
sf = &s->frame;
|
|
/* close the closure variables. */
|
|
close_var_refs(rt, sf);
|
|
if (sf->arg_buf) {
|
|
/* cannot free the function if it is running */
|
|
assert(sf->cur_sp != NULL);
|
|
for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) {
|
|
JS_FreeValueRT(rt, *sp);
|
|
}
|
|
js_free_rt(rt, sf->arg_buf);
|
|
}
|
|
JS_FreeValueRT(rt, sf->cur_func);
|
|
JS_FreeValueRT(rt, s->this_val);
|
|
}
|
|
|
|
JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s)
|
|
{
|
|
JSValue func_obj;
|
|
if (js_check_stack_overflow(ctx->rt, 0))
|
|
return JS_ThrowStackOverflow(ctx);
|
|
/* the tag does not matter provided it is not an object */
|
|
func_obj = JS_MKPTR(JS_TAG_INT, s);
|
|
return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,
|
|
s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR);
|
|
}
|