Fix former workaround by concerning static member functions.
"storage" attribute is removed by Language::staticmemberfunctionHandler which was resolved by a workaround before. Now, the implementation relies completely on state flags retrieved at a proper point. git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/branches/oliverb-javascript-v8@13772 626c5289-ae23-0410-ae9c-e8d60b6d4f22
This commit is contained in:
parent
c470864d12
commit
16077503c8
3 changed files with 40 additions and 274 deletions
|
|
@ -202,17 +202,17 @@ protected:
|
||||||
/**
|
/**
|
||||||
* Generates code for a function.
|
* Generates code for a function.
|
||||||
*/
|
*/
|
||||||
virtual int emitFunction(Node *n, bool is_member) = 0;
|
virtual int emitFunction(Node *n, bool is_member, bool is_static) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates code for a getter function.
|
* Generates code for a getter function.
|
||||||
*/
|
*/
|
||||||
virtual int emitGetter(Node *n, bool is_member) = 0;
|
virtual int emitGetter(Node *n, bool is_member, bool is_static) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates code for a setter function.
|
* Generates code for a setter function.
|
||||||
*/
|
*/
|
||||||
virtual int emitSetter(Node *n, bool is_member) = 0;
|
virtual int emitSetter(Node *n, bool is_member, bool is_static) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to find out if a function is a setter or not.
|
* Helper function to find out if a function is a setter or not.
|
||||||
|
|
@ -315,6 +315,7 @@ int JAVASCRIPT::functionHandler(Node *n) {
|
||||||
int JAVASCRIPT::globalfunctionHandler(Node *n) {
|
int JAVASCRIPT::globalfunctionHandler(Node *n) {
|
||||||
emitter->switchNamespace(n);
|
emitter->switchNamespace(n);
|
||||||
Language::globalfunctionHandler(n);
|
Language::globalfunctionHandler(n);
|
||||||
|
|
||||||
return SWIG_OK;
|
return SWIG_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -326,13 +327,11 @@ int JAVASCRIPT::globalfunctionHandler(Node *n) {
|
||||||
|
|
||||||
int JAVASCRIPT::staticmemberfunctionHandler(Node *n) {
|
int JAVASCRIPT::staticmemberfunctionHandler(Node *n) {
|
||||||
/*
|
/*
|
||||||
* Note: storage=static is not set for static member functions.
|
* Note: storage=static is removed by Language::staticmemberfunctionHandler.
|
||||||
* It also is not working to set that attribute here.
|
* So, don't rely on that after here. Instead use the state variable which is
|
||||||
* Instead the according state variable is set directly.
|
* set by JSEmitter::enterFunction().
|
||||||
*/
|
*/
|
||||||
SetFlag(emitter->getState().function(), IS_STATIC);
|
Language::staticmemberfunctionHandler(n);
|
||||||
Language::staticmemberfunctionHandler(n);
|
|
||||||
Setattr(emitter->getState().function(), IS_STATIC, 0);
|
|
||||||
return SWIG_OK;
|
return SWIG_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -344,14 +343,7 @@ int JAVASCRIPT::staticmemberfunctionHandler(Node *n) {
|
||||||
|
|
||||||
int JAVASCRIPT::variableHandler(Node *n) {
|
int JAVASCRIPT::variableHandler(Node *n) {
|
||||||
|
|
||||||
if (!is_assignable(n)
|
emitter->enterVariable(n);
|
||||||
// FIXME: test "arrays_global" does not compile with that as it is not allowed to assign to char[]
|
|
||||||
// probably some error in char[] typemap
|
|
||||||
|| Equal(Getattr(n, "type"), "a().char")) {
|
|
||||||
SetFlag(n, "wrap:immutable");
|
|
||||||
}
|
|
||||||
|
|
||||||
emitter->enterVariable(n);
|
|
||||||
Language::variableHandler(n);
|
Language::variableHandler(n);
|
||||||
emitter->exitVariable(n);
|
emitter->exitVariable(n);
|
||||||
|
|
||||||
|
|
@ -622,20 +614,18 @@ int JSEmitter::emitWrapperFunction(Node *n) {
|
||||||
if (kind) {
|
if (kind) {
|
||||||
bool is_member = GetFlag(n, "ismember");
|
bool is_member = GetFlag(n, "ismember");
|
||||||
if (Cmp(kind, "function") == 0) {
|
if (Cmp(kind, "function") == 0) {
|
||||||
ret = emitFunction(n, is_member);
|
bool is_static = GetFlag(state.function(), IS_STATIC);
|
||||||
|
ret = emitFunction(n, is_member, is_static);
|
||||||
} else if (Cmp(kind, "variable") == 0) {
|
} else if (Cmp(kind, "variable") == 0) {
|
||||||
|
bool is_static = GetFlag(state.variable(), IS_STATIC);
|
||||||
if (isSetterMethod(n)) {
|
if (isSetterMethod(n)) {
|
||||||
ret = emitSetter(n, is_member);
|
ret = emitSetter(n, is_member, is_static);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
ret = emitGetter(n, is_member);
|
ret = emitGetter(n, is_member, is_static);
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Printf(stderr, "Warning: unsupported wrapper function type\n");
|
Printf(stderr, "Warning: unsupported wrapper function type\n");
|
||||||
Swig_print_node(n);
|
Swig_print_node(n);
|
||||||
|
|
||||||
ret = SWIG_ERROR;
|
ret = SWIG_ERROR;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -643,10 +633,8 @@ int JSEmitter::emitWrapperFunction(Node *n) {
|
||||||
|
|
||||||
if (Cmp(view, "constructorHandler") == 0) {
|
if (Cmp(view, "constructorHandler") == 0) {
|
||||||
ret = emitCtor(n);
|
ret = emitCtor(n);
|
||||||
|
|
||||||
} else if (Cmp(view, "destructorHandler") == 0) {
|
} else if (Cmp(view, "destructorHandler") == 0) {
|
||||||
ret = emitDtor(n);
|
ret = emitDtor(n);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Printf(stderr, "Warning: unsupported wrapper function type");
|
Printf(stderr, "Warning: unsupported wrapper function type");
|
||||||
Swig_print_node(n);
|
Swig_print_node(n);
|
||||||
|
|
@ -677,14 +665,29 @@ int JSEmitter::enterFunction(Node *n) {
|
||||||
|
|
||||||
state.function(true);
|
state.function(true);
|
||||||
state.function(NAME, Getattr(n, "sym:name"));
|
state.function(NAME, Getattr(n, "sym:name"));
|
||||||
|
if(Equal(Getattr(n, "storage"), "static")) {
|
||||||
|
SetFlag(state.function(), IS_STATIC);
|
||||||
|
}
|
||||||
|
|
||||||
return SWIG_OK;
|
return SWIG_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int JSEmitter::enterVariable(Node *n) {
|
int JSEmitter::enterVariable(Node *n) {
|
||||||
|
|
||||||
state.variable(true);
|
state.variable(true);
|
||||||
state.variable(NAME, Swig_scopename_last(Getattr(n, "name")));
|
state.variable(NAME, Swig_scopename_last(Getattr(n, "name")));
|
||||||
state.variable(IS_IMMUTABLE, Getattr(n, "wrap:immutable"));
|
|
||||||
|
if(Equal(Getattr(n, "storage"), "static")) {
|
||||||
|
SetFlag(state.variable(), IS_STATIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Language::instance()->is_assignable(n)
|
||||||
|
// FIXME: test "arrays_global" does not compile with that as it is not allowed to assign to char[]
|
||||||
|
// probably some error in char[] typemap
|
||||||
|
|| Equal(Getattr(n, "type"), "a().char")) {
|
||||||
|
SetFlag(state.variable(), IS_IMMUTABLE);
|
||||||
|
}
|
||||||
|
|
||||||
return SWIG_OK;
|
return SWIG_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -763,13 +766,13 @@ protected:
|
||||||
|
|
||||||
virtual int exitClass(Node *n);
|
virtual int exitClass(Node *n);
|
||||||
|
|
||||||
virtual int emitFunction(Node *n, bool is_member);
|
virtual int emitFunction(Node *n, bool is_member, bool is_static);
|
||||||
|
|
||||||
virtual int emitFunctionDispatcher(Node *n, bool is_member);
|
virtual int emitFunctionDispatcher(Node *n, bool is_member);
|
||||||
|
|
||||||
virtual int emitGetter(Node *n, bool is_member);
|
virtual int emitGetter(Node *n, bool is_member, bool is_static);
|
||||||
|
|
||||||
virtual int emitSetter(Node *n, bool is_member);
|
virtual int emitSetter(Node *n, bool is_member, bool is_static);
|
||||||
|
|
||||||
virtual int emitConstant(Node *n);
|
virtual int emitConstant(Node *n);
|
||||||
|
|
||||||
|
|
@ -1069,7 +1072,7 @@ int JSCEmitter::exitFunction(Node *n) {
|
||||||
.replace("${functionwrapper}", state.function(WRAPPER_NAME));
|
.replace("${functionwrapper}", state.function(WRAPPER_NAME));
|
||||||
|
|
||||||
if (is_member) {
|
if (is_member) {
|
||||||
if (Equal(Getattr(n, "storage"), "static")) {
|
if (GetFlag(state.function(), IS_STATIC)) {
|
||||||
Append(state.clazz(STATIC_FUNCTIONS), t_function.str());
|
Append(state.clazz(STATIC_FUNCTIONS), t_function.str());
|
||||||
} else {
|
} else {
|
||||||
Append(state.clazz(MEMBER_FUNCTIONS), t_function.str());
|
Append(state.clazz(MEMBER_FUNCTIONS), t_function.str());
|
||||||
|
|
@ -1098,7 +1101,8 @@ int JSCEmitter::exitVariable(Node *n) {
|
||||||
.replace("${propertyname}", state.variable(NAME));
|
.replace("${propertyname}", state.variable(NAME));
|
||||||
|
|
||||||
if (GetFlag(n, "ismember")) {
|
if (GetFlag(n, "ismember")) {
|
||||||
if (Equal(Getattr(n, "storage"), "static") || (Equal(Getattr(n, "nodeType"), "enumitem"))) {
|
if (GetFlag(state.function(), IS_STATIC)
|
||||||
|
|| Equal(Getattr(n, "nodeType"), "enumitem")) {
|
||||||
Append(state.clazz(STATIC_VARIABLES), t_variable.str());
|
Append(state.clazz(STATIC_VARIABLES), t_variable.str());
|
||||||
} else {
|
} else {
|
||||||
Append(state.clazz(MEMBER_VARIABLES), t_variable.str());
|
Append(state.clazz(MEMBER_VARIABLES), t_variable.str());
|
||||||
|
|
@ -1229,12 +1233,10 @@ int JSCEmitter::emitDtor(Node *) {
|
||||||
return SWIG_OK;
|
return SWIG_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int JSCEmitter::emitGetter(Node *n, bool is_member) {
|
int JSCEmitter::emitGetter(Node *n, bool is_member, bool is_static) {
|
||||||
Wrapper *wrapper = NewWrapper();
|
Wrapper *wrapper = NewWrapper();
|
||||||
|
|
||||||
Template t_getter(getTemplate("JS_getproperty"));
|
Template t_getter(getTemplate("JS_getproperty"));
|
||||||
bool is_static = Equal(Getattr(n, "storage"), "static");
|
|
||||||
|
|
||||||
// prepare wrapper name
|
// prepare wrapper name
|
||||||
String *wrap_name = Swig_name_wrapper(Getattr(n, "sym:name"));
|
String *wrap_name = Swig_name_wrapper(Getattr(n, "sym:name"));
|
||||||
Setattr(n, "wrap:name", wrap_name);
|
Setattr(n, "wrap:name", wrap_name);
|
||||||
|
|
@ -1261,7 +1263,7 @@ int JSCEmitter::emitGetter(Node *n, bool is_member) {
|
||||||
return SWIG_OK;
|
return SWIG_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int JSCEmitter::emitSetter(Node *n, bool is_member) {
|
int JSCEmitter::emitSetter(Node *n, bool is_member, bool is_static) {
|
||||||
|
|
||||||
// skip variables that are immutable
|
// skip variables that are immutable
|
||||||
if (State::IsSet(state.variable(IS_IMMUTABLE))) {
|
if (State::IsSet(state.variable(IS_IMMUTABLE))) {
|
||||||
|
|
@ -1271,7 +1273,6 @@ int JSCEmitter::emitSetter(Node *n, bool is_member) {
|
||||||
Wrapper *wrapper = NewWrapper();
|
Wrapper *wrapper = NewWrapper();
|
||||||
|
|
||||||
Template t_setter(getTemplate("JS_setproperty"));
|
Template t_setter(getTemplate("JS_setproperty"));
|
||||||
bool is_static = Equal(Getattr(n, "storage"), "static");
|
|
||||||
|
|
||||||
// prepare wrapper name
|
// prepare wrapper name
|
||||||
String *wrap_name = Swig_name_wrapper(Getattr(n, "sym:name"));
|
String *wrap_name = Swig_name_wrapper(Getattr(n, "sym:name"));
|
||||||
|
|
@ -1345,18 +1346,12 @@ int JSCEmitter::emitConstant(Node *n) {
|
||||||
return SWIG_OK;
|
return SWIG_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int JSCEmitter::emitFunction(Node *n, bool is_member) {
|
int JSCEmitter::emitFunction(Node *n, bool is_member, bool is_static) {
|
||||||
|
|
||||||
Wrapper *wrapper = NewWrapper();
|
Wrapper *wrapper = NewWrapper();
|
||||||
|
|
||||||
Template t_function(getTemplate("JS_functionwrapper"));
|
Template t_function(getTemplate("JS_functionwrapper"));
|
||||||
|
|
||||||
// Note: there is an inconsistency in SWIG with static member functions
|
|
||||||
// that do not have storage:static
|
|
||||||
// in these cases the context (staticmemberfunctionHandler) is
|
|
||||||
// exploited and a flag is set temporarily
|
|
||||||
// TODO: this could be done in general with is_member and is_static
|
|
||||||
bool is_static = State::IsSet(state.function(IS_STATIC)) || Equal(Getattr(n, "storage"), "static");
|
|
||||||
bool is_overloaded = GetFlag(n, "sym:overloaded");
|
bool is_overloaded = GetFlag(n, "sym:overloaded");
|
||||||
|
|
||||||
// prepare the function wrapper name
|
// prepare the function wrapper name
|
||||||
|
|
|
||||||
|
|
@ -1,139 +0,0 @@
|
||||||
#ifndef JAVASCRIPT_EMITTER_H
|
|
||||||
#define JAVASCRIPT_EMITTER_H
|
|
||||||
|
|
||||||
#include "swigmod.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class that wraps a code snippet used as template for code generation.
|
|
||||||
*/
|
|
||||||
class Template {
|
|
||||||
|
|
||||||
public:
|
|
||||||
Template(const String* code);
|
|
||||||
|
|
||||||
~Template();
|
|
||||||
|
|
||||||
String* str();
|
|
||||||
|
|
||||||
Template& Replace(const String* pattern, const String* repl);
|
|
||||||
|
|
||||||
private:
|
|
||||||
String* m_code;
|
|
||||||
};
|
|
||||||
|
|
||||||
class JSEmitter {
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
enum JSEmitterType {
|
|
||||||
JavascriptCore,
|
|
||||||
V8,
|
|
||||||
QtScript
|
|
||||||
};
|
|
||||||
|
|
||||||
JSEmitter();
|
|
||||||
|
|
||||||
virtual ~JSEmitter();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens output files and temporary output DOHs.
|
|
||||||
*/
|
|
||||||
virtual int Initialize(Node *n) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes all collected code into the output file(s).
|
|
||||||
*/
|
|
||||||
virtual int Dump(Node *n) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleans up all open output DOHs.
|
|
||||||
*/
|
|
||||||
virtual int Close() = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Switches the context for code generation.
|
|
||||||
*
|
|
||||||
* Classes, global variables and global functions may need to
|
|
||||||
* be registered in certain static tables.
|
|
||||||
* This method should be used to switch output DOHs correspondingly.
|
|
||||||
*/
|
|
||||||
virtual int SwitchContext(Node *n) { return SWIG_OK; };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked at the beginning of the classHandler.
|
|
||||||
*/
|
|
||||||
virtual int EnterClass(Node *n) { return SWIG_OK; };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked at the end of the classHandler.
|
|
||||||
*/
|
|
||||||
virtual int ExitClass(Node *n) { return SWIG_OK; };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked at the beginning of the variableHandler.
|
|
||||||
*/
|
|
||||||
virtual int EnterVariable(Node *n) { return SWIG_OK; };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked at the end of the variableHandler.
|
|
||||||
*/
|
|
||||||
virtual int ExitVariable(Node *n) { return SWIG_OK; };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked at the beginning of the functionHandler.
|
|
||||||
*/
|
|
||||||
virtual int EnterFunction(Node *n) { return SWIG_OK; };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked at the end of the functionHandler.
|
|
||||||
*/
|
|
||||||
virtual int ExitFunction(Node *n) { return SWIG_OK; };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked by functionWrapper callback after call to Language::functionWrapper.
|
|
||||||
*/
|
|
||||||
virtual int EmitWrapperFunction(Node *n);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a given code snippet for a given key name.
|
|
||||||
*
|
|
||||||
* This method is called by the fragmentDirective handler
|
|
||||||
* of the JAVASCRIPT language module.
|
|
||||||
*/
|
|
||||||
int RegisterTemplate(const String *name, const String *code);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the code template registered for a given name.
|
|
||||||
*/
|
|
||||||
const String* GetTemplate(const String *name);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
virtual int EmitCtor(Node *n) = 0;
|
|
||||||
|
|
||||||
virtual int EmitDtor(Node *n) = 0;
|
|
||||||
|
|
||||||
virtual int EmitFunction(Node *n, bool is_member) = 0;
|
|
||||||
|
|
||||||
virtual int EmitGetter(Node *n, bool is_member) = 0;
|
|
||||||
|
|
||||||
virtual int EmitSetter(Node *n, bool is_member) = 0;
|
|
||||||
|
|
||||||
bool IsSetterMethod(Node *n);
|
|
||||||
|
|
||||||
Node* GetBaseClass(Node *n);
|
|
||||||
|
|
||||||
const String* typemapLookup(Node *n, const_String_or_char_ptr tmap_method,
|
|
||||||
SwigType *type, int warning, Node *typemap_attributes = 0);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
// empty string used at different places in the code
|
|
||||||
String *empty_string;
|
|
||||||
|
|
||||||
Hash *templates;
|
|
||||||
|
|
||||||
Wrapper* current_wrapper;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // JAVASCRIPT_EMITTER_H
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
#ifndef JAVASCRIPT_V8_H
|
|
||||||
#define JAVASCRIPT_V8_H
|
|
||||||
|
|
||||||
#include "javascript_emitter.h"
|
|
||||||
|
|
||||||
class V8Emitter: public JSEmitter {
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
V8Emitter();
|
|
||||||
|
|
||||||
virtual ~V8Emitter();
|
|
||||||
|
|
||||||
virtual int Initialize(Node *n);
|
|
||||||
|
|
||||||
virtual int Dump(Node *n);
|
|
||||||
|
|
||||||
virtual int Close();
|
|
||||||
|
|
||||||
virtual int SwitchContext(Node *n);
|
|
||||||
|
|
||||||
virtual int EnterClass(Node *n);
|
|
||||||
|
|
||||||
virtual int ExitClass(Node *n);
|
|
||||||
|
|
||||||
virtual int EnterVariable(Node *n);
|
|
||||||
|
|
||||||
virtual int ExitVariable(Node *n);
|
|
||||||
|
|
||||||
virtual int EnterFunction(Node *n);
|
|
||||||
|
|
||||||
virtual int ExitFunction(Node *n);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
int CreateNamespace(String* scope);
|
|
||||||
|
|
||||||
virtual int EmitCtor(Node *n);
|
|
||||||
|
|
||||||
virtual int EmitDtor(Node *n);
|
|
||||||
|
|
||||||
virtual int EmitFunction(Node *n, bool is_member);
|
|
||||||
|
|
||||||
virtual int EmitGetter(Node *n, bool is_member);
|
|
||||||
|
|
||||||
virtual int EmitSetter(Node *n, bool is_member);
|
|
||||||
|
|
||||||
void marshalInputArgs(Node *n, ParmList *parms, int numarg, Wrapper *wrapper);
|
|
||||||
|
|
||||||
void marshalOutput(Node *n, String *actioncode, Wrapper *wrapper);
|
|
||||||
|
|
||||||
Parm *skipIgnoredArgs(Parm *p);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
File *f_runtime;
|
|
||||||
File *f_header;
|
|
||||||
File *f_class_templates;
|
|
||||||
File *f_wrapper;
|
|
||||||
|
|
||||||
File *f_init_namespaces;
|
|
||||||
File *f_init_class_templates;
|
|
||||||
File *f_init_wrappers;
|
|
||||||
File *f_init_inheritance;
|
|
||||||
File *f_init_class_instances;
|
|
||||||
File *f_init_static_wrappers;
|
|
||||||
File *f_init_register_classes;
|
|
||||||
File *f_init_register_namespaces;
|
|
||||||
|
|
||||||
// the output cpp file
|
|
||||||
File *f_wrap_cpp;
|
|
||||||
|
|
||||||
// state variables
|
|
||||||
String* current_context;
|
|
||||||
String* current_class_type;
|
|
||||||
String* current_classname_mangled;
|
|
||||||
String* current_classname_unqualified;
|
|
||||||
String* current_variable_mangled;
|
|
||||||
String* current_variable_unqualified;
|
|
||||||
String* current_getter;
|
|
||||||
String* current_setter;
|
|
||||||
String* current_function_mangled;
|
|
||||||
String* current_function_unqualified;
|
|
||||||
|
|
||||||
String* GLOBAL;
|
|
||||||
String* NULL_STR;
|
|
||||||
Hash* namespaces;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // JAVASCRIPT_V8_H
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue