I want to know which file called the class:
database.ts
class Database {
constructor() {
console.log(`This class was called by ${super()}`);
}
}
server.ts
import Database from 'database.ts';
new Database(); // The constructor would print "This class was called by server.ts"
Of course the way I presented by using "super()" doesn't work, but which way does ? I could not find an answer for this question. Is it possible to do such thing ?
Log the superclass name
You can log the name of the superclass like this:
Object.getPrototypeOf(Object.getPrototypeOf(foo)).constructor.name;
see https://stackoverflow.com/a/44251889/675721
Logging the superclass name will not show you which file called instantiated your class, however.
Log the calling file
To do this you should create an log an error.
class Database {
constructor() {
const a = new Error();
console.log(a);
}
}
This will give you a stack trace with all of the file names.
Error
at REPL39:1:23
at Script.runInThisContext (vm.js:132:18)
at REPLServer.defaultEval (repl.js:479:29)
at bound (domain.js:430:14)
at REPLServer.runBound [as eval] (domain.js:443:12)
at REPLServer.onLine (repl.js:809:10)
at REPLServer.emit (events.js:326:22)
at REPLServer.EventEmitter.emit (domain.js:486:12)
at REPLServer.Interface._onLine (readline.js:337:10)
at REPLServer.Interface._line (readline.js:666:8)
Debugger
Neither of these is really the best approach - you should use a debugger to set a breakpoint when the class is instantiated or when the database isn't working and look around to see what is different.
It isn't strictly a typescript question. There is no simple way to do this but you can hack around with the stack trace I believe. On that way you can find out where your class has been instantiated even if your application is packaged and the file name information is lost. The stack trace can be obtained by processing the callee property of the constructor's arguments. E.g. see this question for a possible solution. I.e.
function stacktrace() {
function st2(f) {
return !f ? [] :
st2(f.caller).concat([f.toString().split('(')[0].substring(9) + '(' + f.arguments.join(',') + ')']);
}
return st2(arguments.callee.caller);
}
But you can make your own version of this function what returns the stack as an array. I assume from the comments on the question you would like to store the caller in the class and log it in case of error so storing the trace as an object might suit your needs better.
Then just call this function from the Database class constructor.
Of course I could add a parameter to the constructor. But is there another way ?
class Database() {
constructor(fileLocation: string) {
console.log(`This file was called by ${fileLocation}`);
}
}
Related
it's two days that I am having a problem hooking and calling getPackageInfo using Frida.
I have wrote this code:
Java.perform(()=>{
const jPM=Java.use('android.app.ApplicationPackageManager');
const getPkgInfo=jPM.getPackageInfo.overload('java.lang.String','int');
getPkgInfo.implementation=(pname,f)=>{
console.log("Called => getPackageInfo (Flag="+f+", Pkg="+pname+")");
return getPkgInfo(this,pname,f);
}
});
At the line that calls getPkgInfo (return getPkgInfo(this,pname,f);) i get this error:
Called => getPackageInfo (Flag=0, Pkg=test.app)
{"type":"error","description":"TypeError: cannot read property '$getHandle' of undefined","stack":"TypeError: cannot read property '$getHandle' of undefined
at value (frida/node_modules/frida-java-bridge/lib/class-factory.js:1052)
at e (frida/node_modules/frida-java-bridge/lib/class-factory.js:585)
at <anonymous> (/script.js:42)
at apply (native)
at ne (frida/node_modules/frida-java-bridge/lib/class-factory.js:619)
at <anonymous> (frida/node_modules/frida-java-bridge/lib/class-factory.js:597)","fileName":"frida/node_modules/frida-java-bridge/lib/class-factory.js","lineNumber":1052,"columnNumber":1}
So the method is hooked, because i can see the Called => getPackageInfo (Flag=0, Pkg=test.app) string, but i can't call the original method.
This happens only when i try hooking methods that are normally called using the abstract variant (in this case android.content.pm.PackageManager.getPackageInfo, but i have also tried hooking getPackageManager).
I have made different versions of this code, for example one that calls the function in this way: this.getPackageInfo.overload('java.lang.String','int').call(this,pname,f); and i get an error saying that "this" is undefined, so i think that it is the problem.
I have tried this script on different apps and devices but i always get the same result.
I am using Frida Inject.
Please help me.
The following webPreferences properties are enabled in the BrowserWindow Object I created:
{
webPreferences:
{
nodeIntegration:false,
enableRemoteModule:false,
contextIsolation:true,
worldSafeExecuteJavaScript:true,
preload:path.join(__dirname,"preload.js"),
webSecurity:true,
webviewTag:false,
allowRunningInsecureContent:false,
nodeIntegrationInSubFrames:false,
nodeIntegrationInWorker:false,
}
}
My preload.ts:
electron.contextBridge.exposeInMainWorld(
"someapiname",
{
requireClass(): any
{
return require("path/to/module").ClassName
}
}
)
My HTML file loads a Script called html.js, which contains:
(function()
{
const CLASSNAME = window["someapiname"].requireClass()
const newclass = new CLASSNAME("some param",3)
newclass.aRandomMethod()
})()
But in the Developer Console of Electron I always get:
Uncaught Error: Uncaught TypeError: Class constructor CLASSNAME cannot be invoked without 'new'
I don't get it...It asks for the new but IT is there? Is there something I don't know or some kind of restriction of Electron (and no, I do not want nodeIntegration to be set to true and any other harmful options like enableRemoteModule)
There's no problem when I create the class in the preload script, but then the methods of the Class go missing. I can only access the properties. I also tried it with that IpcMain and IpcRenderer but I got a different Error (something with Parameter).
I just want to create a Class in the renderer and use its methods.
https://www.electronjs.org/docs/api/context-bridge#parameter--error--return-type-support
It explicitly states you cannot copy classes / ctors via contextBridge.
Prototype modifications are dropped. Sending classes or constructors will not work.
i am trying to put the user defined logic (Javascript) in frontend and my backend function execute it with 'Function' method (as below)
let cmdResult = Function('"use strict"; let _AW_DAY_SUMMARY_=' + JSON.stringify(_AW_DAY_SUMMARY_) + ';' + sUserDefinedCmd)()
...
this.alicia.dump(); // run the code correctly if hardcoded in script. But hit error below when defined by user frontpage
The user defined logic work correctly, and i was able to push in object with tons of values with JSON.stringify method. But i have a challenge as i have loaded quite a number of classes
constructor(
private alicia: AliciaService,
private benny: BennyService,
private cass: CassService,
) { }
How could the user defined logic to allow call like this.alicia.dump();
Whenever i use it in my user defined logic, it complained
ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'alicia' of undefined
There are many business process intensive method in those classes and i need the user defined logic to call and return rather than re-write from scratch.
The execution context of function that was created through Function constructor is a global object: window in your case.
In order to change this context you specify with each context to execute your function through Function.prototype.call():
Function('...').call(this)
Stackblitz Example
I am using a custom javascript modulue which has it's own Error objects. I would like to intercept those custom Error objects and take the appropriate path in my try{} catch{} block, distinguishing them from Javascript's built in Error objects such as ReferenceError, TypeError etc.
So a bit like this.
try {
// Some code that might produce a traditional javascript error
// or one of the errors raised by the module I am using.
}catch (error){
if(error instanceof ExchangeError){
// Handle this in a way.
}else{
// Probably one of the built in Javascript errors,
// So do this other thing.
}
}
So, in the example above, ExchangeError is a custom error belonging to that specific module, however, I am not able to run the instanceof on my error, despite the fact that when I do error.constructor.name I get ExchangeError.
My javascript scope simply does not know about that ExchangeError. So the question is, how can I intercept those kind of Error objects? I'm sure I can do it with string matching, but just wanted to check if there is a more elegant way.
One thing I tried, I have my own errors module, that has some custom errors in there, I tried to mimic the module's Error object:
class ExchangeError extends Error {
constructor (message) {
super (message);
this.constructor = ExchangeError;
this.__proto__ = ExchangeError.prototype;
this.message = message;
}
}
and import that through my errors module, but that did not work obviously.
By actually implementing my own ExchangeError I actually was doing something really really bad, I was blinding the instanceof check with my own ExchangeError, whereas the ExchangeError instance that was coming from the module, was NOT an instance of my own ExchangeError. That is why my if check was falling silent.
The solution is simply doing this:
const { ExchangeError } = require ('ccxt/js/base/errors');
Importing the error from within the module. Now the instanceof look up is working. I did not know that one can import bits and pieces from a module like that.
Thanks to #FrankerZ for pointing that out.
I'm guessing the answer to my question is no, but since I don't know enough about how Error.prototype works I figured it was worth asking: is there any way to change the error messages from the errors in Javascript?
For instance, if I get an error:
TypeError: (intermediate value) is not iterable
is there any way to change things such that I instead get:
TypeError: You expected an array but it wasn't an array dummy!
I thought about using a global error handler and then rethrowing them, but that would only work for uncaught errors. Is there any Error.prototype method I can change (or any other way) to do this?
Not at all important, just curious.
EDIT: Just to clarify two points:
1) I understand how try/catch works, but what I'm asking about is whether there is a way to control the messages generated before the error is thrown/caught (presumably by overwriting a method of Error.prototype).
2) An answer of "no there is no way to do this, all generating of JS error messages is handled internally and the JS code has no way to control it" would be perfectly legitimate (... if that's the case).
You have to override the TypeError class, not one of these methods.
const overridableMessages = [{
search: 'TypeError: (intermediate value) is not iterable',
replace: 'TypeError: You expected an array but it wasn\'t an array dummy!'
}]
class TypeError extends window.TypeError {
constructor (message) {
super(message)
overridableMessages.forEach((overridableMessage) => {
if (this.message === overridableMessage.search) {
this.message = overridableMessage.replace
}
})
}
}
window.TypeError = TypeError
window.onerror = function(msg, url, linenumber) {alert('Error : '+msg+'\nURL: '+url+'\nLine Number: '+linenumber);return true;}
// general error handler
Also:
try {
}
catch(err) {
alert(err); // here put your case with your messages.
}
// routine error handler
HIH,
I hate to answer my own question, but I found this line on the MDN which seems to pretty clearly answer things:
The global Error object contains no methods of its own, however, it does inherit some methods through the prototype chain.
Since the only methods it gets through the prototype chain are toString and toSource (neither of which controls an error's generated message), it appears (unless I'm missing some other mechanism that isn't Error.prototype-related) that there is no way to accomplish what I asked.