summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Marques <george@gmarqu.es>2018-05-29 23:16:56 -0300
committerGeorge Marques <george@gmarqu.es>2018-07-20 21:55:17 -0300
commit4b18c4e448c93fbb44c80b89e744cfacea8d8bc4 (patch)
tree82ee55b5fd0cc3fbe112344b029a5d5563094592
parent743053734f187c220250d88e4e475b7a87767cbc (diff)
Add typed instructions to GDScript
- Typed assignment (built-in, native, and script). - Cast (built-in conversion; native and script checks). - Check type of functions arguments on call. - Check type of members on set.
-rw-r--r--modules/gdscript/gdscript.cpp6
-rw-r--r--modules/gdscript/gdscript_compiler.cpp171
-rw-r--r--modules/gdscript/gdscript_function.cpp223
-rw-r--r--modules/gdscript/gdscript_function.h79
4 files changed, 469 insertions, 10 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index ae8dcfc370..a8916c2a2e 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -941,8 +941,12 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
if (err.error == Variant::CallError::CALL_OK) {
return true; //function exists, call was successful
}
- } else
+ } else {
+ if (!E->get().data_type.is_type(p_value)) {
+ return false; // Type mismatch
+ }
members[E->get().index] = p_value;
+ }
return true;
}
}
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index d65b22cc8c..46ff52f42b 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -423,7 +423,80 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
} break;
case GDScriptParser::Node::TYPE_CAST: {
const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
- return _parse_expression(codegen, cn->source_node, p_stack_level);
+
+ int slevel = p_stack_level;
+ int src_addr = _parse_expression(codegen, cn->source_node, slevel);
+ if (src_addr < 0)
+ return src_addr;
+ if (src_addr & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
+
+ switch (cn->cast_type.kind) {
+ case GDScriptParser::DataType::BUILTIN: {
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_BUILTIN);
+ codegen.opcodes.push_back(cn->cast_type.builtin_type);
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ int class_idx;
+ if (GDScriptLanguage::get_singleton()->get_global_map().has(cn->cast_type.native_type)) {
+
+ class_idx = GDScriptLanguage::get_singleton()->get_global_map()[cn->cast_type.native_type];
+ class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
+ } else {
+ _set_error("Invalid native class type '" + String(cn->cast_type.native_type) + "'.", cn);
+ return -1;
+ }
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_NATIVE); // perform operator
+ codegen.opcodes.push_back(class_idx); // variable type
+ } break;
+ case GDScriptParser::DataType::CLASS: {
+
+ Variant script;
+ int idx = -1;
+ if (cn->cast_type.class_type->name == StringName()) {
+ script = codegen.script;
+ } else {
+ StringName name = cn->cast_type.class_type->name;
+ if (class_map[name] == codegen.script->subclasses[name]) {
+ idx = codegen.get_name_map_pos(name);
+ idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS;
+ } else {
+ script = class_map[name];
+ }
+ }
+
+ if (idx < 0) {
+ idx = codegen.get_constant_pos(script);
+ idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
+ }
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_SCRIPT); // perform operator
+ codegen.opcodes.push_back(idx); // variable type
+ } break;
+ case GDScriptParser::DataType::SCRIPT:
+ case GDScriptParser::DataType::GDSCRIPT: {
+
+ Variant script = cn->cast_type.script_type;
+ int idx = codegen.get_constant_pos(script);
+ idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_SCRIPT); // perform operator
+ codegen.opcodes.push_back(idx); // variable type
+ } break;
+ default: {
+ _set_error("Parser bug: unresolved data type.", cn);
+ return -1;
+ }
+ }
+
+ codegen.opcodes.push_back(src_addr); // source adddress
+ int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
+ codegen.alloc_stack(p_stack_level);
+ return dst_addr;
+
} break;
case GDScriptParser::Node::TYPE_OPERATOR: {
//hell breaks loose
@@ -1051,12 +1124,87 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
if (src_address_b < 0)
return -1;
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
- codegen.opcodes.push_back(dst_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
+ GDScriptParser::DataType assign_type = on->arguments[0]->get_datatype();
+
+ if (assign_type.has_type && !on->arguments[1]->get_datatype().has_type) {
+ // Typed assignment
+ switch (assign_type.kind) {
+ case GDScriptParser::DataType::BUILTIN: {
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); // perform operator
+ codegen.opcodes.push_back(assign_type.builtin_type); // variable type
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ int class_idx;
+ if (GDScriptLanguage::get_singleton()->get_global_map().has(assign_type.native_type)) {
+
+ class_idx = GDScriptLanguage::get_singleton()->get_global_map()[assign_type.native_type];
+ class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
+ } else {
+ _set_error("Invalid native class type '" + String(assign_type.native_type) + "'.", on->arguments[0]);
+ return -1;
+ }
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE); // perform operator
+ codegen.opcodes.push_back(class_idx); // variable type
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2
+ } break;
+ case GDScriptParser::DataType::CLASS: {
+
+ Variant script;
+ int idx = -1;
+ if (assign_type.class_type->name == StringName()) {
+ script = codegen.script;
+ } else {
+ StringName name = assign_type.class_type->name;
+ if (class_map[name] == codegen.script->subclasses[name]) {
+ idx = codegen.get_name_map_pos(name);
+ idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS;
+ } else {
+ script = class_map[name];
+ }
+ }
+
+ if (idx < 0) {
+ idx = codegen.get_constant_pos(script);
+ idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
+ }
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT); // perform operator
+ codegen.opcodes.push_back(idx); // variable type
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2
+ } break;
+ case GDScriptParser::DataType::SCRIPT:
+ case GDScriptParser::DataType::GDSCRIPT: {
+
+ Variant script = assign_type.script_type;
+ int idx = codegen.get_constant_pos(script);
+ idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT); // perform operator
+ codegen.opcodes.push_back(idx); // variable type
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2
+ } break;
+ default: {
+ ERR_PRINT("Compiler bug: unresolved assign.");
+
+ // Shouldn't get here, but fail-safe to a regular assignment
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
+ }
+ }
+ } else {
+ // Either untyped assignment or already type-checked by the parser
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
+ }
return dst_address_a; //if anything, returns wathever was assigned or correct stack position
}
-
} break;
case GDScriptParser::OperatorNode::OP_IS: {
@@ -1513,6 +1661,17 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
gdfunc->_static = p_func->_static;
gdfunc->rpc_mode = p_func->rpc_mode;
gdfunc->argument_types.resize(p_func->argument_types.size());
+ for (int i = 0; i < p_func->argument_types.size(); i++) {
+ gdfunc->argument_types[i] = _gdtype_from_datatype(p_func->argument_types[i]);
+ }
+ gdfunc->return_type = _gdtype_from_datatype(p_func->return_type);
+ } else {
+ gdfunc->_static = false;
+ gdfunc->rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ gdfunc->return_type = GDScriptDataType();
+ gdfunc->return_type.has_type = true;
+ gdfunc->return_type.kind = GDScriptDataType::BUILTIN;
+ gdfunc->return_type.builtin_type = Variant::NIL;
}
#ifdef TOOLS_ENABLED
@@ -1843,7 +2002,6 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
p_script->subclasses.insert(name, subclass);
}
- p_script->valid = true;
return OK;
}
@@ -1950,6 +2108,7 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
}
}
+ p_script->valid = true;
return OK;
}
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index 319400bbbe..6a08d86904 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -200,6 +200,12 @@ static String _get_var_type(const Variant *p_type) {
&&OPCODE_ASSIGN, \
&&OPCODE_ASSIGN_TRUE, \
&&OPCODE_ASSIGN_FALSE, \
+ &&OPCODE_ASSIGN_TYPED_BUILTIN, \
+ &&OPCODE_ASSIGN_TYPED_NATIVE, \
+ &&OPCODE_ASSIGN_TYPED_SCRIPT, \
+ &&OPCODE_CAST_TO_BUILTIN, \
+ &&OPCODE_CAST_TO_NATIVE, \
+ &&OPCODE_CAST_TO_SCRIPT, \
&&OPCODE_CONSTRUCT, \
&&OPCODE_CONSTRUCT_ARRAY, \
&&OPCODE_CONSTRUCT_DICTIONARY, \
@@ -318,10 +324,28 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (_stack_size) {
stack = (Variant *)aptr;
- for (int i = 0; i < p_argcount; i++)
- memnew_placement(&stack[i], Variant(*p_args[i]));
- for (int i = p_argcount; i < _stack_size; i++)
+ for (int i = 0; i < p_argcount; i++) {
+ if (!argument_types[i].has_type) {
+ memnew_placement(&stack[i], Variant(*p_args[i]));
+ continue;
+ }
+
+ if (!argument_types[i].is_type(*p_args[i], true)) {
+ r_err.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_err.argument = i;
+ r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT;
+ return Variant();
+ }
+ if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
+ Variant arg = Variant::construct(argument_types[i].builtin_type, &p_args[i], 1, r_err);
+ memnew_placement(&stack[i], Variant(arg));
+ } else {
+ memnew_placement(&stack[i], Variant(*p_args[i]));
+ }
+ }
+ for (int i = p_argcount; i < _stack_size; i++) {
memnew_placement(&stack[i], Variant);
+ }
} else {
stack = NULL;
}
@@ -709,6 +733,199 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
+ OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) {
+
+ CHECK_SPACE(4);
+ Variant::Type var_type = (Variant::Type)_code_ptr[ip + 1];
+ GET_VARIANT_PTR(dst, 2);
+ GET_VARIANT_PTR(src, 3);
+
+ GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
+
+ if (src->get_type() != var_type) {
+ err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
+ "' to a variable of type '" + Variant::get_type_name(var_type) + "'.";
+ OPCODE_BREAK;
+ }
+
+ *dst = *src;
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) {
+
+ CHECK_SPACE(4);
+ GET_VARIANT_PTR(type, 1);
+ GET_VARIANT_PTR(dst, 2);
+ GET_VARIANT_PTR(src, 3);
+
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
+ GD_ERR_BREAK(!nc);
+ Object *src_obj = src->operator Object *();
+ GD_ERR_BREAK(!src_obj);
+
+ if (!ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
+ err_text = "Trying to assign value of type '" + src_obj->get_class_name() +
+ "' to a variable of type '" + nc->get_name() + "'.";
+ OPCODE_BREAK;
+ }
+
+ *dst = *src;
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) {
+
+ CHECK_SPACE(4);
+ GET_VARIANT_PTR(type, 1);
+ GET_VARIANT_PTR(dst, 2);
+ GET_VARIANT_PTR(src, 3);
+
+ Script *base_type = Object::cast_to<Script>(type->operator Object *());
+
+ GD_ERR_BREAK(!base_type);
+
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+
+ if (src->get_type() != Variant::NIL && src->operator Object *() != NULL) {
+
+ ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
+ if (!scr_inst) {
+ err_text = "Trying to assign value of type '" + src->operator Object *()->get_class_name() +
+ "' to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+
+ Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
+ bool valid = false;
+
+ while (src_type) {
+ if (src_type == base_type) {
+ valid = true;
+ break;
+ }
+ src_type = src_type->get_base_script().ptr();
+ }
+
+ if (!valid) {
+ err_text = "Trying to assign value of type '" + src->operator Object *()->get_script_instance()->get_script()->get_path().get_file() +
+ "' to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+ }
+
+ *dst = *src;
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_BUILTIN) {
+
+ CHECK_SPACE(4);
+ Variant::Type to_type = (Variant::Type)_code_ptr[ip + 1];
+ GET_VARIANT_PTR(src, 2);
+ GET_VARIANT_PTR(dst, 3);
+
+ GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX);
+
+ Variant::CallError err;
+ *dst = Variant::construct(to_type, (const Variant **)&src, 1, err);
+
+#ifdef DEBUG_ENABLED
+ if (err.error != Variant::CallError::CALL_OK) {
+ err_text = "Invalid cast: could not convert value to '" + Variant::get_type_name(to_type) + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_NATIVE) {
+
+ CHECK_SPACE(4);
+ GET_VARIANT_PTR(to_type, 1);
+ GET_VARIANT_PTR(src, 2);
+ GET_VARIANT_PTR(dst, 3);
+
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(to_type->operator Object *());
+ GD_ERR_BREAK(!nc);
+
+#ifdef DEBUG_ENABLED
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Invalid cast: can't convert a non-object value to an object type.";
+ OPCODE_BREAK;
+ }
+#endif
+ Object *src_obj = src->operator Object *();
+
+ if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
+ *dst = Variant(); // invalid cast, assign NULL
+ } else {
+ *dst = *src;
+ }
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_SCRIPT) {
+
+ CHECK_SPACE(4);
+ GET_VARIANT_PTR(to_type, 1);
+ GET_VARIANT_PTR(src, 2);
+ GET_VARIANT_PTR(dst, 3);
+
+ Script *base_type = Object::cast_to<Script>(to_type->operator Object *());
+
+ GD_ERR_BREAK(!base_type);
+
+#ifdef DEBUG_ENABLED
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+
+ bool valid = false;
+
+ if (src->get_type() != Variant::NIL && src->operator Object *() != NULL) {
+
+ ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
+
+ if (scr_inst) {
+
+ Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
+
+ while (src_type) {
+ if (src_type == base_type) {
+ valid = true;
+ break;
+ }
+ src_type = src_type->get_base_script().ptr();
+ }
+ }
+ }
+
+ if (valid) {
+ *dst = *src; // Valid cast, copy the source object
+ } else {
+ *dst = Variant(); // invalid cast, assign NULL
+ }
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
OPCODE(OPCODE_CONSTRUCT) {
CHECK_SPACE(2);
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 62b6871b76..3ce84290fd 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -54,6 +54,79 @@ struct GDScriptDataType {
StringName native_type;
Ref<Script> script_type;
+ bool is_type(const Variant &p_variant, bool p_allow_implicit_conversion = false) const {
+ if (!has_type) return true; // Can't type check
+
+ switch (kind) {
+ case BUILTIN: {
+ Variant::Type var_type = p_variant.get_type();
+ bool valid = builtin_type == var_type;
+ if (!valid && p_allow_implicit_conversion) {
+ valid = Variant::can_convert_strict(var_type, builtin_type);
+ }
+ return valid;
+ } break;
+ case NATIVE: {
+ if (p_variant.get_type() == Variant::NIL) {
+ return true;
+ }
+ if (p_variant.get_type() != Variant::OBJECT) {
+ return false;
+ }
+ Object *obj = p_variant.operator Object *();
+ if (obj && !ClassDB::is_parent_class(obj->get_class_name(), native_type)) {
+ return false;
+ }
+ return true;
+ } break;
+ case SCRIPT:
+ case GDSCRIPT: {
+ if (p_variant.get_type() == Variant::NIL) {
+ return true;
+ }
+ if (p_variant.get_type() != Variant::OBJECT) {
+ return false;
+ }
+ Object *obj = p_variant.operator Object *();
+ Ref<Script> base = obj && obj->get_script_instance() ? obj->get_script_instance()->get_script() : NULL;
+ bool valid = false;
+ while (base.is_valid()) {
+ if (base == script_type) {
+ valid = true;
+ break;
+ }
+ base = base->get_base_script();
+ }
+ return valid;
+ } break;
+ }
+ return false;
+ }
+
+ operator PropertyInfo() const {
+ PropertyInfo info;
+ if (has_type) {
+ switch (kind) {
+ case BUILTIN: {
+ info.type = builtin_type;
+ } break;
+ case NATIVE: {
+ info.type = Variant::OBJECT;
+ info.class_name = native_type;
+ } break;
+ case SCRIPT:
+ case GDSCRIPT: {
+ info.type = Variant::OBJECT;
+ info.class_name = script_type->get_instance_base_type();
+ } break;
+ }
+ } else {
+ info.type = Variant::NIL;
+ info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
+ return info;
+ }
+
GDScriptDataType() :
has_type(false) {}
};
@@ -72,6 +145,12 @@ public:
OPCODE_ASSIGN,
OPCODE_ASSIGN_TRUE,
OPCODE_ASSIGN_FALSE,
+ OPCODE_ASSIGN_TYPED_BUILTIN,
+ OPCODE_ASSIGN_TYPED_NATIVE,
+ OPCODE_ASSIGN_TYPED_SCRIPT,
+ OPCODE_CAST_TO_BUILTIN,
+ OPCODE_CAST_TO_NATIVE,
+ OPCODE_CAST_TO_SCRIPT,
OPCODE_CONSTRUCT, //only for basic types!!
OPCODE_CONSTRUCT_ARRAY,
OPCODE_CONSTRUCT_DICTIONARY,