blob: 34ba4302601798ed4528ab84190d0573a7c0bfa4 [file] [edit]
/*
* GPAC - Multimedia Framework C SDK
*
* Authors: Jean Le Feuvre
* Copyright (c) Telecom ParisTech 2000-2026
* All rights reserved
*
* This file is part of GPAC / BIFS codec sub-project
*
* GPAC is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* GPAC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include "script.h"
#if !defined(GPAC_DISABLE_BIFS) && defined(GPAC_HAS_QJS)
#define BINOP_MINVAL ET_EQ
typedef struct
{
GF_Node *script;
GF_BifsDecoder *codec;
GF_BitStream *bs;
char *string;
u32 length;
GF_List *identifiers;
char *new_line;
u32 indent;
u32 expr_stack_size;
} ScriptParser;
void SFS_Identifier(ScriptParser *parser);
void SFS_Arguments(ScriptParser *parser, Bool is_var);
void SFS_StatementBlock(ScriptParser *parser, Bool functBody);
void SFS_Statement(ScriptParser *parser);
void SFS_IfStatement(ScriptParser *parser);
void SFS_SwitchStatement(ScriptParser *parser);
void SFS_ForStatement(ScriptParser *parser);
void SFS_WhileStatement(ScriptParser *parser);
void SFS_ReturnStatement(ScriptParser *parser);
void SFS_CompoundExpression(ScriptParser *parser);
void SFS_OptionalExpression(ScriptParser *parser);
void SFS_Expression(ScriptParser *parser);
void SFS_NewObject(ScriptParser *parser);
void SFS_ArrayDeref(ScriptParser *parser);
void SFS_FunctionCall(ScriptParser *parser);
void SFS_ObjectMemberAccess(ScriptParser *parser);
void SFS_ObjectMethodCall(ScriptParser *parser);
void SFS_Params(ScriptParser *parser);
void SFS_GetNumber(ScriptParser *parser);
void SFS_GetString(ScriptParser *parser);
void SFS_GetBoolean(ScriptParser *parser);
#define PARSER_STEP_ALLOC 500
static void SFS_AddString(ScriptParser *parser, char *str)
{
if (!str) return;
u32 c_str = (u32) strlen(parser->string);
u32 a_str = (u32) strlen(str);
if (c_str + a_str >= parser->length) {
parser->length = c_str + a_str + PARSER_STEP_ALLOC;
parser->string = (char *)gf_realloc(parser->string, sizeof(char)*parser->length);
}
gf_strlcat(parser->string, str, parser->length);
}
static void SFS_AddInt(ScriptParser *parser, s32 val)
{
char msg[500];
sprintf(msg, "%d", val);
SFS_AddString(parser, msg);
}
static void SFS_AddChar(ScriptParser *parser, char c)
{
char msg[2];
sprintf(msg, "%c", c);
SFS_AddString(parser, msg);
}
GF_Err ParseScriptField(ScriptParser *parser)
{
GF_ScriptField *field;
GF_Err e;
u32 eventType, fieldType;
char name[1000];
GF_FieldInfo info;
eventType = gf_bs_read_int(parser->bs, 2);
fieldType = gf_bs_read_int(parser->bs, 6);
gf_bifs_dec_name(parser->bs, name, 1000);
field = gf_sg_script_field_new(parser->script, eventType, fieldType, name);
if (!field) return GF_NON_COMPLIANT_BITSTREAM;
//save the name in the list of identifiers
gf_list_add(parser->identifiers, gf_strdup(name));
if (parser->codec->pCurrentProto) {
Bool isISfield = (Bool)gf_bs_read_int(parser->bs, 1);
if (isISfield) {
u32 numProtoField = gf_sg_proto_get_field_count(parser->codec->pCurrentProto);
u32 numBits = gf_get_bit_size(numProtoField - 1);
u32 field_all = gf_bs_read_int(parser->bs, numBits);
e = gf_sg_script_field_get_info(field, &info);
if (e) return e;
e = gf_sg_proto_field_set_ised(parser->codec->pCurrentProto, field_all, parser->script, info.fieldIndex);
return e;
}
}
/*get default value*/
if (eventType == GF_SG_SCRIPT_TYPE_FIELD) {
if (gf_bs_read_int(parser->bs, 1)) {
e = gf_sg_script_field_get_info(field, &info);
if (e) return e;
gf_bifs_dec_field(parser->codec, parser->bs, parser->script, &info, GF_FALSE);
}
}
return parser->codec->LastError;
}
static void SFS_IncIndent(ScriptParser *pars) {
pars->indent++;
}
static void SFS_DecIndent(ScriptParser *pars) {
pars->indent--;
}
static void SFS_Space(ScriptParser *pars) {
if (pars->new_line) SFS_AddString(pars, " ");
}
static void SFS_Indent(ScriptParser *pars)
{
u32 i;
if (pars->new_line) {
for (i=0; i<pars->indent; i++) SFS_AddString(pars, " ");
}
}
static GFINLINE void SFS_Line(ScriptParser *parser)
{
if (parser->new_line) {
SFS_AddString(parser, parser->new_line);
}
}
GF_Err SFScript_Parse(GF_BifsDecoder *codec, SFScript *script_field, GF_BitStream *bs, GF_Node *n)
{
GF_Err e;
u32 i, count, nbBits;
char *ptr;
ScriptParser parser;
e = GF_OK;
if (gf_node_get_tag(n) != TAG_MPEG4_Script) return GF_NON_COMPLIANT_BITSTREAM;
memset(&parser, 0, sizeof(ScriptParser));
parser.codec = codec;
parser.script = n;
parser.bs = bs;
parser.length = 500;
parser.string = (char *) gf_malloc(sizeof(char)* parser.length);
parser.string[0] = 0;
parser.identifiers = gf_list_new();
parser.new_line = (char *) (codec->dec_memory_mode ? "\n" : NULL);
parser.indent = 0;
//first parse fields
if (gf_bs_read_int(bs, 1)) {
//endFlag
while (!gf_bs_read_int(bs, 1)) {
e = ParseScriptField(&parser);
if (e) goto exit;
}
} else {
nbBits = gf_bs_read_int(bs, 4);
count = gf_bs_read_int(bs, nbBits);
for (i=0; i<count; i++) {
e = ParseScriptField(&parser);
if (e) goto exit;
}
}
//reserevd
gf_bs_read_int(bs, 1);
//then parse
SFS_AddString(&parser, "javascript:");
SFS_AddString(&parser, parser.new_line);
//hasFunction
while (gf_bs_read_int(bs, 1)) {
SFS_AddString(&parser, "function ");
SFS_Identifier(&parser);
SFS_Arguments(&parser, GF_FALSE);
SFS_Space(&parser);
SFS_StatementBlock(&parser, GF_TRUE);
SFS_Line(&parser);
if (codec->LastError) {
e = codec->LastError;
goto exit;
}
}
SFS_Line(&parser);
if (script_field->script_text) gf_free(script_field->script_text);
script_field->script_text = (char *) gf_strdup(parser.string);
exit:
//clean up
while (gf_list_count(parser.identifiers)) {
ptr = (char *)gf_list_get(parser.identifiers, 0);
gf_free(ptr);
gf_list_rem(parser.identifiers, 0);
}
gf_list_del(parser.identifiers);
if (parser.string) gf_free(parser.string);
return e;
}
void SFS_Identifier(ScriptParser *parser)
{
u32 index;
char name[500];
if (parser->codec->LastError) return;
//received
if (gf_bs_read_int(parser->bs, 1)) {
index = gf_bs_read_int(parser->bs, gf_get_bit_size(gf_list_count(parser->identifiers) - 1));
SFS_AddString(parser, (char *)gf_list_get(parser->identifiers, index));
}
//parse
else {
gf_bifs_dec_name(parser->bs, name, 500);
gf_list_add(parser->identifiers, gf_strdup(name));
SFS_AddString(parser, name);
}
}
void SFS_Arguments(ScriptParser *parser, Bool is_var)
{
u32 val;
if (parser->codec->LastError) return;
if (!is_var) SFS_AddString(parser, "(");
val = gf_bs_read_int(parser->bs, 1);
while (val) {
SFS_Identifier(parser);
val = gf_bs_read_int(parser->bs, 1);
if (val) SFS_AddString(parser, ",");
}
if (!is_var) SFS_AddString(parser, ")");
}
void SFS_StatementBlock(ScriptParser *parser, Bool funcBody)
{
if (parser->codec->LastError) return;
if (gf_bs_read_int(parser->bs, 1)) {
SFS_AddString(parser, "{");
SFS_IncIndent(parser);
while (gf_bs_read_int(parser->bs, 1)) {
SFS_Line(parser);
SFS_Indent(parser);
SFS_Statement(parser);
}
SFS_DecIndent(parser);
SFS_Line(parser);
SFS_Indent(parser);
SFS_AddString(parser, "}");
} else if (funcBody) {
SFS_AddString(parser, "{");
SFS_Statement(parser);
SFS_AddString(parser, "}");
} else {
SFS_Statement(parser);
}
}
void SFS_Statement(ScriptParser *parser)
{
u32 val;
if (parser->codec->LastError) return;
val = gf_bs_read_int(parser->bs, NUMBITS_STATEMENT);
switch (val) {
case ST_IF:
SFS_IfStatement(parser);
break;
case ST_FOR:
SFS_ForStatement(parser);
break;
case ST_WHILE:
SFS_WhileStatement(parser);
break;
case ST_RETURN:
SFS_ReturnStatement(parser);
break;
case ST_BREAK:
SFS_AddString(parser, "break;");
break;
case ST_CONTINUE:
SFS_AddString(parser, "continue;");
break;
case ST_COMPOUND_EXPR:
SFS_CompoundExpression(parser);
SFS_AddString(parser, ";");
break;
case ST_SWITCH:
SFS_SwitchStatement(parser);
break;
}
}
void SFS_IfStatement(ScriptParser *parser)
{
if (parser->codec->LastError) return;
SFS_AddString(parser, "if (");
SFS_CompoundExpression(parser);
SFS_AddString(parser, ") ");
SFS_StatementBlock(parser, GF_FALSE);
//has else
if (gf_bs_read_int(parser->bs, 1)) {
SFS_Line(parser);
SFS_Indent(parser);
SFS_AddString(parser, "else ");
SFS_StatementBlock(parser, GF_FALSE);
}
}
void SFS_SwitchStatement(ScriptParser *parser)
{
u32 numBits, caseValue;
if (parser->codec->LastError) return;
if (!gf_bs_available(parser->bs)) {
parser->codec->LastError = GF_NON_COMPLIANT_BITSTREAM;
return;
}
SFS_AddString(parser, "switch (");
SFS_CompoundExpression(parser);
SFS_AddString(parser, ")");
SFS_AddString(parser, "{");
SFS_Line(parser);
numBits = gf_bs_read_int(parser->bs, 5);
do {
SFS_Indent(parser);
SFS_AddString(parser, "case ");
caseValue = gf_bs_read_int(parser->bs, numBits);
SFS_AddInt(parser, caseValue);
SFS_AddString(parser, ":");
SFS_Line(parser);
SFS_Indent(parser);
SFS_StatementBlock(parser, GF_FALSE);
SFS_Line(parser);
}
while (gf_bs_read_int(parser->bs, 1));
//default
if (gf_bs_read_int(parser->bs, 1)) {
SFS_AddString(parser, "default:");
SFS_Line(parser);
SFS_StatementBlock(parser, GF_FALSE);
}
SFS_AddString(parser, "}");
}
void SFS_ForStatement(ScriptParser *parser)
{
if (parser->codec->LastError) return;
SFS_AddString(parser, "for (");
SFS_OptionalExpression(parser);
SFS_AddString(parser, ";");
SFS_OptionalExpression(parser);
SFS_AddString(parser, ";");
SFS_OptionalExpression(parser);
SFS_AddString(parser, ")");
SFS_StatementBlock(parser, GF_FALSE);
}
void SFS_WhileStatement(ScriptParser *parser)
{
if (parser->codec->LastError) return;
SFS_AddString(parser, "while (");
SFS_CompoundExpression(parser);
SFS_AddString(parser, ")");
SFS_StatementBlock(parser, GF_FALSE);
}
void SFS_ReturnStatement(ScriptParser *parser)
{
if (parser->codec->LastError) return;
SFS_AddString(parser, "return");
if (gf_bs_read_int(parser->bs, 1)) {
SFS_AddString(parser, " ");
SFS_CompoundExpression(parser);
}
SFS_AddString(parser, ";");
SFS_Line(parser);
}
void SFS_CompoundExpression(ScriptParser *parser)
{
if (parser->codec->LastError) return;
SFS_Expression(parser);
if (! gf_bs_read_int(parser->bs, 1)) return;
if (parser->codec->LastError) return;
SFS_AddString(parser, ",");
SFS_CompoundExpression(parser);
}
void SFS_OptionalExpression(ScriptParser *parser)
{
if (parser->codec->LastError) return;
if (gf_bs_read_int(parser->bs, 1)) {
SFS_CompoundExpression(parser);
}
}
#define MAX_EXPR_STACK 500
void SFS_Expression(ScriptParser *parser)
{
if (!gf_bs_available(parser->bs)) {
parser->codec->LastError = GF_NON_COMPLIANT_BITSTREAM;
return;
}
u32 val = gf_bs_read_int(parser->bs, NUMBITS_EXPR_TYPE);
if (parser->codec->LastError) return;
//limit max expression stack size
parser->expr_stack_size++;
if (parser->expr_stack_size>MAX_EXPR_STACK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[BIFS] Max stack size %d reached for expressions, not supported\n", MAX_EXPR_STACK))
parser->codec->LastError = GF_NON_COMPLIANT_BITSTREAM;
return;
}
switch(val) {
case ET_CURVED_EXPR:
SFS_AddString(parser, "(");
SFS_CompoundExpression(parser);
SFS_AddString(parser, ")");
break;
case ET_NEGATIVE:
SFS_AddString(parser, "-");
SFS_Expression(parser);
break;
case ET_NOT:
SFS_AddString(parser, "!");
SFS_Expression(parser);
break;
case ET_ONESCOMP:
SFS_AddString(parser, "~");
SFS_Expression(parser);
break;
case ET_INCREMENT:
SFS_AddString(parser, "++");
SFS_Expression(parser);
break;
case ET_DECREMENT:
SFS_AddString(parser, "--");
SFS_Expression(parser);
break;
case ET_POST_INCREMENT:
SFS_Expression(parser);
SFS_AddString(parser, "++");
break;
case ET_POST_DECREMENT:
SFS_Expression(parser);
SFS_AddString(parser, "--");
break;
case ET_CONDTEST:
SFS_Expression(parser);
SFS_AddString(parser, " ? ");
SFS_Expression(parser);
SFS_AddString(parser, " : ");
SFS_Expression(parser);
break;
case ET_STRING:
SFS_AddString(parser, "'");
SFS_GetString(parser);
SFS_AddString(parser, "'");
break;
case ET_NUMBER:
SFS_GetNumber(parser);
break;
case ET_IDENTIFIER:
SFS_Identifier(parser);
break;
case ET_FUNCTION_CALL:
SFS_FunctionCall(parser);
break;
case ET_NEW:
SFS_NewObject(parser);
break;
case ET_OBJECT_MEMBER_ACCESS:
SFS_ObjectMemberAccess(parser);
break;
case ET_OBJECT_METHOD_CALL:
SFS_ObjectMethodCall(parser);
break;
case ET_ARRAY_DEREFERENCE:
SFS_ArrayDeref(parser);
break;
case ET_MULTIPLY:
SFS_Expression(parser);
SFS_AddString(parser, "*");
SFS_Expression(parser);
break;
case ET_DIVIDE:
SFS_Expression(parser);
SFS_AddString(parser, "/");
SFS_Expression(parser);
break;
case ET_MOD:
SFS_Expression(parser);
SFS_AddString(parser, "%");
SFS_Expression(parser);
break;
case ET_PLUS:
SFS_Expression(parser);
SFS_AddString(parser, "+");
SFS_Expression(parser);
break;
case ET_MINUS:
SFS_Expression(parser);
SFS_AddString(parser, "-");
SFS_Expression(parser);
break;
case ET_LSHIFT:
SFS_Expression(parser);
SFS_AddString(parser, "<<");
SFS_Expression(parser);
break;
case ET_RSHIFT:
SFS_Expression(parser);
SFS_AddString(parser, ">>");
SFS_Expression(parser);
break;
case ET_RSHIFTFILL:
SFS_Expression(parser);
SFS_AddString(parser, ">>>");
SFS_Expression(parser);
break;
case ET_AND:
SFS_Expression(parser);
SFS_AddString(parser, "&");
SFS_Expression(parser);
break;
case ET_XOR:
SFS_Expression(parser);
SFS_AddString(parser, "^");
SFS_Expression(parser);
break;
case ET_OR:
SFS_Expression(parser);
SFS_AddString(parser, "|");
SFS_Expression(parser);
break;
case ET_LT:
SFS_Expression(parser);
SFS_AddString(parser, "<");
SFS_Expression(parser);
break;
case ET_LE:
SFS_Expression(parser);
SFS_AddString(parser, "<=");
SFS_Expression(parser);
break;
case ET_GT:
SFS_Expression(parser);
SFS_AddString(parser, ">");
SFS_Expression(parser);
break;
case ET_GE:
SFS_Expression(parser);
SFS_AddString(parser, ">=");
SFS_Expression(parser);
break;
case ET_EQ:
SFS_Expression(parser);
SFS_AddString(parser, "==");
SFS_Expression(parser);
break;
case ET_NE:
SFS_Expression(parser);
SFS_AddString(parser, "!=");
SFS_Expression(parser);
break;
case ET_LAND:
SFS_Expression(parser);
SFS_AddString(parser, "&&");
SFS_Expression(parser);
break;
case ET_LOR:
SFS_Expression(parser);
SFS_AddString(parser, "||");
SFS_Expression(parser);
break;
case ET_ASSIGN:
SFS_Expression(parser);
SFS_AddString(parser, "=");
SFS_Expression(parser);
break;
case ET_PLUSEQ:
SFS_Expression(parser);
SFS_AddString(parser, "+=");
SFS_Expression(parser);
break;
case ET_MINUSEQ:
SFS_Expression(parser);
SFS_AddString(parser, "-=");
SFS_Expression(parser);
break;
case ET_MULTIPLYEQ:
SFS_Expression(parser);
SFS_AddString(parser, "*=");
SFS_Expression(parser);
break;
case ET_DIVIDEEQ:
SFS_Expression(parser);
SFS_AddString(parser, "/=");
SFS_Expression(parser);
break;
case ET_MODEQ:
SFS_Expression(parser);
SFS_AddString(parser, "%=");
SFS_Expression(parser);
break;
case ET_LSHIFTEQ:
SFS_Expression(parser);
SFS_AddString(parser, "<<=");
SFS_Expression(parser);
break;
case ET_RSHIFTEQ:
SFS_Expression(parser);
SFS_AddString(parser, ">>=");
SFS_Expression(parser);
break;
case ET_RSHIFTFILLEQ:
SFS_Expression(parser);
SFS_AddString(parser, ">>>=");
SFS_Expression(parser);
break;
case ET_ANDEQ:
SFS_Expression(parser);
SFS_AddString(parser, "&=");
SFS_Expression(parser);
break;
case ET_XOREQ:
SFS_Expression(parser);
SFS_AddString(parser, "^=");
SFS_Expression(parser);
break;
case ET_OREQ:
SFS_Expression(parser);
SFS_AddString(parser, "|=");
SFS_Expression(parser);
break;
case ET_BOOLEAN:
SFS_GetBoolean(parser);
break;
case ET_VAR:
SFS_AddString(parser, "var ");
SFS_Arguments(parser, GF_TRUE);
break;
case ET_FUNCTION_ASSIGN:
SFS_AddString(parser, "function ");
SFS_Arguments(parser, GF_FALSE);
SFS_StatementBlock(parser, GF_TRUE);
break;
default:
parser->codec->LastError = GF_NON_COMPLIANT_BITSTREAM;
break;
}
parser->expr_stack_size--;
}
void SFS_NewObject(ScriptParser *parser)
{
if (parser->codec->LastError) return;
SFS_AddString(parser, "new ");
SFS_Identifier(parser);
SFS_AddString(parser, "(");
SFS_Params(parser);
SFS_AddString(parser, ") ");
}
void SFS_ArrayDeref(ScriptParser *parser)
{
if (parser->codec->LastError) return;
SFS_Expression(parser);
if (parser->codec->LastError) return;
SFS_AddString(parser, "[");
SFS_CompoundExpression(parser);
SFS_AddString(parser, "]");
}
void SFS_FunctionCall(ScriptParser *parser)
{
if (parser->codec->LastError) return;
SFS_Identifier(parser);
SFS_AddString(parser, "(");
SFS_Params(parser);
SFS_AddString(parser, ")");
}
void SFS_ObjectMemberAccess(ScriptParser *parser)
{
if (parser->codec->LastError) return;
SFS_Expression(parser);
if (parser->codec->LastError) return;
SFS_AddString(parser, ".");
SFS_Identifier(parser);
}
void SFS_ObjectMethodCall(ScriptParser *parser)
{
if (parser->codec->LastError) return;
SFS_Expression(parser);
if (parser->codec->LastError) return;
SFS_AddString(parser, ".");
SFS_Identifier(parser);
SFS_AddString(parser, "(");
SFS_Params(parser);
SFS_AddString(parser, ")");
}
void SFS_Params(ScriptParser *parser)
{
u32 val;
if (parser->codec->LastError) return;
val = gf_bs_read_int(parser->bs, 1);
while (val) {
SFS_Expression(parser);
if (parser->codec->LastError) return;
val = gf_bs_read_int(parser->bs, 1);
if(val) SFS_AddString(parser, ",");
}
}
void SFS_GetNumber(ScriptParser *parser)
{
u32 val, nbBits;
if (parser->codec->LastError) return;
if (!gf_bs_available(parser->bs)) {
parser->codec->LastError = GF_NON_COMPLIANT_BITSTREAM;
return;
}
// integer
if (gf_bs_read_int(parser->bs, 1)) {
nbBits = gf_bs_read_int(parser->bs, 5);
val = gf_bs_read_int(parser->bs, nbBits);
SFS_AddInt(parser, val);
return;
}
// real number
val = gf_bs_read_int(parser->bs, 4);
while ( val != 15) {
if (val<=9) {
SFS_AddChar(parser, (char) (val + '0') );
} else if (val==10) {
SFS_AddChar(parser, '.');
} else if (val==11) {
SFS_AddChar(parser, 'E');
} else if (val==12) {
SFS_AddChar(parser, '-');
}
if (!gf_bs_available(parser->bs)) {
parser->codec->LastError = GF_NON_COMPLIANT_BITSTREAM;
return;
}
val = gf_bs_read_int(parser->bs, 4);
}
}
void SFS_GetString(ScriptParser *parser)
{
char name[1000];
if (parser->codec->LastError) return;
gf_bifs_dec_name(parser->bs, name, 1000);
SFS_AddString(parser, name);
}
void SFS_GetBoolean(ScriptParser *parser)
{
if (parser->codec->LastError) return;
if (gf_bs_read_int(parser->bs, 1)) {
SFS_AddString(parser, "true");
} else {
SFS_AddString(parser, "false");
}
}
#endif /*!defined(GPAC_DISABLE_BIFS) && defined(GPAC_HAS_QJS)*/