_ü^ø^{"files":{"node_modules":{"files":{"@electron":{"files":{"remote":{"files":{"LICENSE":{"size":1070,"offset":"0","integrity":{"algorithm":"SHA256","hash":"fc9ae4f3237a307eb111ef8f55bef41d90857e8d1500e592feb4e48639de7a82","blockSize":4194304,"blocks":["fc9ae4f3237a307eb111ef8f55bef41d90857e8d1500e592feb4e48639de7a82"]}},"dist":{"files":{"src":{"files":{"common":{"files":{"get-electron-binding.js":{"size":441,"offset":"1070","integrity":{"algorithm":"SHA256","hash":"f857a19eec43c9f8d97736c86057064c5b71fc14f6e773d1d7b1c268598bd309","blockSize":4194304,"blocks":["f857a19eec43c9f8d97736c86057064c5b71fc14f6e773d1d7b1c268598bd309"]}},"ipc-messages.js":{"size":77,"offset":"1511","integrity":{"algorithm":"SHA256","hash":"d43aa81f5bc89faa359e0f97c814ba25155591ff078fbb9bfd40f8c7c9683230","blockSize":4194304,"blocks":["d43aa81f5bc89faa359e0f97c814ba25155591ff078fbb9bfd40f8c7c9683230"]}},"module-names.js":{"size":1520,"offset":"1588","integrity":{"algorithm":"SHA256","hash":"dfcea2c5575d2db51cb38b22c6b4958337746e6d75ba827f450a57b196856c16","blockSize":4194304,"blocks":["dfcea2c5575d2db51cb38b22c6b4958337746e6d75ba827f450a57b196856c16"]}},"type-utils.js":{"size":4050,"offset":"3108","integrity":{"algorithm":"SHA256","hash":"4e4e7bda21b242e517b9b1b582a15a45e8135bc7f7b1f0e9b33e56181dea3ee8","blockSize":4194304,"blocks":["4e4e7bda21b242e517b9b1b582a15a45e8135bc7f7b1f0e9b33e56181dea3ee8"]}}}},"main":{"files":{"index.js":{"size":535,"offset":"7158","integrity":{"algorithm":"SHA256","hash":"ae4b7e86235189c9a7ee079f4a7bddf7baacbdcc7d213f853ac32bc7ddeafcde","blockSize":4194304,"blocks":["ae4b7e86235189c9a7ee079f4a7bddf7baacbdcc7d213f853ac32bc7ddeafcde"]}},"objects-registry.js":{"size":4442,"offset":"7693","integrity":{"algorithm":"SHA256","hash":"0312d21bd8da83d0384deab4c87cf7d8f3bbfa8424cd65e07e259d3044afd8d9","blockSize":4194304,"blocks":["0312d21bd8da83d0384deab4c87cf7d8f3bbfa8424cd65e07e259d3044afd8d9"]}},"server.js":{"size":21822,"offset":"12135","integrity":{"algorithm":"SHA256","hash":"520423ed3cad887c987493d4a8617dc8086223ace4e67cffd71beffbcda8e7a7","blockSize":4194304,"blocks":["520423ed3cad887c987493d4a8617dc8086223ace4e67cffd71beffbcda8e7a7"]}}}},"renderer":{"files":{"callbacks-registry.js":{"size":2104,"offset":"33957","integrity":{"algorithm":"SHA256","hash":"14db4469fabeed645433a21fabe124f07a7b46e115829b170e9bb16aa77d5992","blockSize":4194304,"blocks":["14db4469fabeed645433a21fabe124f07a7b46e115829b170e9bb16aa77d5992"]}},"index.js":{"size":793,"offset":"36061","integrity":{"algorithm":"SHA256","hash":"f459072159108ef7cc53cb8ca2bb1ce32d752393808165df92ef3708cfe01f54","blockSize":4194304,"blocks":["f459072159108ef7cc53cb8ca2bb1ce32d752393808165df92ef3708cfe01f54"]}},"remote.js":{"size":15820,"offset":"36854","integrity":{"algorithm":"SHA256","hash":"f495b4730d590ae7f52361f973b639041b78ed457477ce52333c2ab8e3045c43","blockSize":4194304,"blocks":["f495b4730d590ae7f52361f973b639041b78ed457477ce52333c2ab8e3045c43"]}}}}}}}},"main":{"files":{"index.js":{"size":45,"offset":"52674","integrity":{"algorithm":"SHA256","hash":"289679196b4b9ea379488a1e810e108f697b7c7e4e4c3a303f403af35482eb87","blockSize":4194304,"blocks":["289679196b4b9ea379488a1e810e108f697b7c7e4e4c3a303f403af35482eb87"]}}}},"package.json":{"size":900,"offset":"52719","integrity":{"algorithm":"SHA256","hash":"5fcbbaf53286aee1bfadaa002ff5da0ce8c3d5e2f0a11679f3075ed3f4962fd2","blockSize":4194304,"blocks":["5fcbbaf53286aee1bfadaa002ff5da0ce8c3d5e2f0a11679f3075ed3f4962fd2"]}},"renderer":{"files":{"index.js":{"size":49,"offset":"53619","integrity":{"algorithm":"SHA256","hash":"2d84fc7d883b14e0f88b9ab83ff28a162a8d10ccfa4f758f940184941d531b82","blockSize":4194304,"blocks":["2d84fc7d883b14e0f88b9ab83ff28a162a8d10ccfa4f758f940184941d531b82"]}}}}}}}},"btime":{"unpacked":true,"files":{"build":{"unpacked":true,"files":{"Release":{"unpacked":true,"files":{"binding.exp":{"size":975,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"4d0c2d06c978adadf2d5b7130b4eaa6e70d48e27ee6039904f37bbeb0f61311a","blockSize":4194304,"blocks":["4d0c2d06c978adadf2d5b7130b4eaa6e70d48e27ee6039904f37bbeb0f61311a"]}},"binding.iobj":{"size":403545,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"2f87c618479ec82dec96acecdd6c0376693d14c32d807a5efd6441fdaa159e65","blockSize":4194304,"blocks":["2f87c618479ec82dec96acecdd6c0376693d14c32d807a5efd6441fdaa159e65"]}},"binding.ipdb":{"size":126816,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"18fa3f8325a63c5f5f0cf16f8937be942f9832cf5376acc33ba955329acc55a3","blockSize":4194304,"blocks":["18fa3f8325a63c5f5f0cf16f8937be942f9832cf5376acc33ba955329acc55a3"]}},"binding.lib":{"size":2080,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"c73a197ced8a99469ebbf508418895d7501a7e0803991271fa9b41cbf3abe71f","blockSize":4194304,"blocks":["c73a197ced8a99469ebbf508418895d7501a7e0803991271fa9b41cbf3abe71f"]}},"nothing.lib":{"size":102582,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"daa75d0e228e3533f04103cc90db215da259ab285c1a842eab84cd59a61cd733","blockSize":4194304,"blocks":["daa75d0e228e3533f04103cc90db215da259ab285c1a842eab84cd59a61cd733"]}},"obj":{"unpacked":true,"files":{"binding":{"unpacked":true,"files":{"binding.node.recipe":{"size":311,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"6e5aa7215148b5607bc39bb7fb4298f232f193af37a740ad4592e5e8da661c8d","blockSize":4194304,"blocks":["6e5aa7215148b5607bc39bb7fb4298f232f193af37a740ad4592e5e8da661c8d"]}},"binding.tlog":{"unpacked":true,"files":{"CL.command.1.tlog":{"size":6792,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"e3e3b897112e85d0595f09acb8eb7a0ef0016a3401750583818b8ee135726060","blockSize":4194304,"blocks":["e3e3b897112e85d0595f09acb8eb7a0ef0016a3401750583818b8ee135726060"]}},"CL.read.1.tlog":{"size":62994,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"92b85fcd00c935c02fecea5c9b49623cd2165844f7d127fa69b86c60d2549571","blockSize":4194304,"blocks":["92b85fcd00c935c02fecea5c9b49623cd2165844f7d127fa69b86c60d2549571"]}},"CL.write.1.tlog":{"size":918,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"062226b03413dfc8d3b625b66492eacd544affe83d4cc55aed52c05bc3ef3b91","blockSize":4194304,"blocks":["062226b03413dfc8d3b625b66492eacd544affe83d4cc55aed52c05bc3ef3b91"]}},"Cl.items.tlog":{"size":454,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"779a9a5b8e34bb78b12d9b4ace7956c2f7be6075ea00501a7b45cff002c53126","blockSize":4194304,"blocks":["779a9a5b8e34bb78b12d9b4ace7956c2f7be6075ea00501a7b45cff002c53126"]}},"binding.lastbuildstate":{"size":179,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"ba9e93631ff05d3cf58e9273ce228a6b6ea23859e2b47c865c8e3b463f67747a","blockSize":4194304,"blocks":["ba9e93631ff05d3cf58e9273ce228a6b6ea23859e2b47c865c8e3b463f67747a"]}},"link.command.1.tlog":{"size":1962,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"755fd1a3c31473ef8bca16098fff4bc7db83402407cc604aef3b94ea19c5fafd","blockSize":4194304,"blocks":["755fd1a3c31473ef8bca16098fff4bc7db83402407cc604aef3b94ea19c5fafd"]}},"link.read.1.tlog":{"size":5574,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"cc0de602a7a7c1d38a267697633759846cc3f93950fee32e5cc0cdebd2734b73","blockSize":4194304,"blocks":["cc0de602a7a7c1d38a267697633759846cc3f93950fee32e5cc0cdebd2734b73"]}},"link.secondary.1.tlog":{"size":520,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"ec8f5b752140cb65ae27272ec8e0cea6e4bcf4852ecd8d971fb5bfccd7b84f76","blockSize":4194304,"blocks":["ec8f5b752140cb65ae27272ec8e0cea6e4bcf4852ecd8d971fb5bfccd7b84f76"]}},"link.write.1.tlog":{"size":764,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"ad48cc32e9ca4c7ccf7cbf8741ade18cd1aec6507eb142641ec65cca4e8145db","blockSize":4194304,"blocks":["ad48cc32e9ca4c7ccf7cbf8741ade18cd1aec6507eb142641ec65cca4e8145db"]}}}}}},"copy":{"unpacked":true,"files":{"copy.recipe":{"size":443,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"9f791d59fc324f70cfffed2ccf3e9e8ef6351d2b3bdda19b4e24f77afc812eed","blockSize":4194304,"blocks":["9f791d59fc324f70cfffed2ccf3e9e8ef6351d2b3bdda19b4e24f77afc812eed"]}},"copy.tlog":{"unpacked":true,"files":{"Cl.items.tlog":{"size":317,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"4caff1ba3e64f20ca64605edfe72ecd7c2fe8b1e3a13a706ecb219a28680abcb","blockSize":4194304,"blocks":["4caff1ba3e64f20ca64605edfe72ecd7c2fe8b1e3a13a706ecb219a28680abcb"]}},"CustomBuild.command.1.tlog":{"size":334,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"50f6dc4565ce0ce5545b672dbd7d8f2bf757db6d498f056891262daf53e1c47e","blockSize":4194304,"blocks":["50f6dc4565ce0ce5545b672dbd7d8f2bf757db6d498f056891262daf53e1c47e"]}},"CustomBuild.read.1.tlog":{"size":71,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"b4412fbbdfde7ddc20156a772562b099ce20d49fec90788dc52dcf6383d51829","blockSize":4194304,"blocks":["b4412fbbdfde7ddc20156a772562b099ce20d49fec90788dc52dcf6383d51829"]}},"CustomBuild.write.1.tlog":{"size":127,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"e9b6de391ac93e59e78b1801222b2f7368de86e347bd91abb25c3ddda303ff27","blockSize":4194304,"blocks":["e9b6de391ac93e59e78b1801222b2f7368de86e347bd91abb25c3ddda303ff27"]}},"copy.lastbuildstate":{"size":179,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"ba9e93631ff05d3cf58e9273ce228a6b6ea23859e2b47c865c8e3b463f67747a","blockSize":4194304,"blocks":["ba9e93631ff05d3cf58e9273ce228a6b6ea23859e2b47c865c8e3b463f67747a"]}}}}}}}},"binding.node":{"size":135168,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"4cbfb569d7ca58bd3af49e06e5688412aad848af816743a079edd4a60f77005a","blockSize":4194304,"blocks":["4cbfb569d7ca58bd3af49e06e5688412aad848af816743a079edd4a60f77005a"]}}}},"binding.vcxproj":{"size":12746,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"fb4254976baf834a5f18aac3715ccd7289ffd59cf69d599a872955042a5a4d80","blockSize":4194304,"blocks":["fb4254976baf834a5f18aac3715ccd7289ffd59cf69d599a872955042a5a4d80"]}},"binding.vcxproj.filters":{"size":3530,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"0fa3edef799480dfc89553123902f660e8d757cc4149c2b6d4929d16fdd88a9d","blockSize":4194304,"blocks":["0fa3edef799480dfc89553123902f660e8d757cc4149c2b6d4929d16fdd88a9d"]}},"copy.vcxproj":{"size":12106,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"3e5e52107f24e8edabff50d8c0cd5bbfb60bc3e22def8ffd84d41b6e9c428ad6","blockSize":4194304,"blocks":["3e5e52107f24e8edabff50d8c0cd5bbfb60bc3e22def8ffd84d41b6e9c428ad6"]}},"copy.vcxproj.filters":{"size":4563,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"5e32e231ebce058e959dcfb8d193344367f4196b6de46a100fe17282bd01f936","blockSize":4194304,"blocks":["5e32e231ebce058e959dcfb8d193344367f4196b6de46a100fe17282bd01f936"]}}}},"index.js":{"size":1726,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"7ade5c334d6eb8a2e5ef8b6d52c5396641903af30c3c2ceda5e4934e3b88f35c","blockSize":4194304,"blocks":["7ade5c334d6eb8a2e5ef8b6d52c5396641903af30c3c2ceda5e4934e3b88f35c"]}},"package.json":{"size":407,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"0db31b9e4d259fe4975a30ae751b08a5a52423cbed7c64cb992e0e66063a4320","blockSize":4194304,"blocks":["0db31b9e4d259fe4975a30ae751b08a5a52423cbed7c64cb992e0e66063a4320"]}},"binding.node":{"size":135168,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"4cbfb569d7ca58bd3af49e06e5688412aad848af816743a079edd4a60f77005a","blockSize":4194304,"blocks":["4cbfb569d7ca58bd3af49e06e5688412aad848af816743a079edd4a60f77005a"]}}}},"get-fonts":{"unpacked":true,"files":{"build":{"unpacked":true,"files":{"Release":{"unpacked":true,"files":{"binding.exp":{"size":983,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"7c7f7bdd911736e9c8a9821d9517ce1623e6bff1b6931c0fd73439d7ea635d91","blockSize":4194304,"blocks":["7c7f7bdd911736e9c8a9821d9517ce1623e6bff1b6931c0fd73439d7ea635d91"]}},"binding.iobj":{"size":633813,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"5478460890fc80fbbc92b764caece7de975388deedd90444d46e8826ca129e45","blockSize":4194304,"blocks":["5478460890fc80fbbc92b764caece7de975388deedd90444d46e8826ca129e45"]}},"binding.ipdb":{"size":205616,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"75f8b20f67f0e78f7891d7ebf36e86e5c76e19ad8b06c74834640fcd83aad182","blockSize":4194304,"blocks":["75f8b20f67f0e78f7891d7ebf36e86e5c76e19ad8b06c74834640fcd83aad182"]}},"binding.lib":{"size":2080,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"c73a197ced8a99469ebbf508418895d7501a7e0803991271fa9b41cbf3abe71f","blockSize":4194304,"blocks":["c73a197ced8a99469ebbf508418895d7501a7e0803991271fa9b41cbf3abe71f"]}},"nothing.lib":{"size":102630,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"16c7417c95adacce89c97a792eb161c6848be1be1b5f5a2b3f5efc926928c81e","blockSize":4194304,"blocks":["16c7417c95adacce89c97a792eb161c6848be1be1b5f5a2b3f5efc926928c81e"]}},"obj":{"unpacked":true,"files":{"binding":{"unpacked":true,"files":{"binding.node.recipe":{"size":315,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"3415184322c0e09d3acbfecce1f8ee1678273be6d31f9ed22b219cfb802387d2","blockSize":4194304,"blocks":["3415184322c0e09d3acbfecce1f8ee1678273be6d31f9ed22b219cfb802387d2"]}},"binding.tlog":{"unpacked":true,"files":{"CL.command.1.tlog":{"size":9888,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"eb90fc0de181d811272c7fbfa51b7cafffc813f2b5edb316ffe3650c53e76699","blockSize":4194304,"blocks":["eb90fc0de181d811272c7fbfa51b7cafffc813f2b5edb316ffe3650c53e76699"]}},"CL.read.1.tlog":{"size":78174,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"22348e7e7f42b91d772cc615b7abe8d02b019fd7dfe1bfc3f334398692c23a89","blockSize":4194304,"blocks":["22348e7e7f42b91d772cc615b7abe8d02b019fd7dfe1bfc3f334398692c23a89"]}},"CL.write.1.tlog":{"size":1234,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"121878915a87ac2591b24e7fbfbc01289f3f14a6fb00994c442afecc6506d9b9","blockSize":4194304,"blocks":["121878915a87ac2591b24e7fbfbc01289f3f14a6fb00994c442afecc6506d9b9"]}},"Cl.items.tlog":{"size":610,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"2d0ec0abc88b25adb9a1f618b1ecbedea4b83abba736187475cfc360ec4ec3cb","blockSize":4194304,"blocks":["2d0ec0abc88b25adb9a1f618b1ecbedea4b83abba736187475cfc360ec4ec3cb"]}},"binding.lastbuildstate":{"size":183,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"1d43546aec834e786961c4a9775b28540d1b9c78e28749ea90874cc61d4219d2","blockSize":4194304,"blocks":["1d43546aec834e786961c4a9775b28540d1b9c78e28749ea90874cc61d4219d2"]}},"link.command.1.tlog":{"size":2272,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"2e1eb59c2819acc11b9de6311439a575d8e707d45dfa0059d25344793738b2da","blockSize":4194304,"blocks":["2e1eb59c2819acc11b9de6311439a575d8e707d45dfa0059d25344793738b2da"]}},"link.read.1.tlog":{"size":6114,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"8b1466cace95c53d5095db9fba167139a825256ad5c600c0bedba3984330e492","blockSize":4194304,"blocks":["8b1466cace95c53d5095db9fba167139a825256ad5c600c0bedba3984330e492"]}},"link.secondary.1.tlog":{"size":633,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"97ac555fd5940b963cd3a46268bbc120169d976c857a72979469cde9dc65bfd9","blockSize":4194304,"blocks":["97ac555fd5940b963cd3a46268bbc120169d976c857a72979469cde9dc65bfd9"]}},"link.write.1.tlog":{"size":974,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"8a3b43d35437a34709af6554fd2ea33382261dcc80250683c4646bc4c8fc202c","blockSize":4194304,"blocks":["8a3b43d35437a34709af6554fd2ea33382261dcc80250683c4646bc4c8fc202c"]}}}}}},"copy":{"unpacked":true,"files":{"copy.recipe":{"size":451,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"dae67a0d731957ab15b7be17df96528b7d7eb1e1e5aa42659ee34244bdd0453b","blockSize":4194304,"blocks":["dae67a0d731957ab15b7be17df96528b7d7eb1e1e5aa42659ee34244bdd0453b"]}},"copy.tlog":{"unpacked":true,"files":{"Cl.items.tlog":{"size":321,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"4d88c0077008ec642cb568c494797032dea4650bdc95be339c69bbf6f53282d8","blockSize":4194304,"blocks":["4d88c0077008ec642cb568c494797032dea4650bdc95be339c69bbf6f53282d8"]}},"CustomBuild.command.1.tlog":{"size":350,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"21bc38102eb0daa953e4498e1fdc6391751a0a9dd0719604b650112aa958b461","blockSize":4194304,"blocks":["21bc38102eb0daa953e4498e1fdc6391751a0a9dd0719604b650112aa958b461"]}},"CustomBuild.read.1.tlog":{"size":75,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"2fa880edd74153a393bab30ffb49669cb2d817dcc76fa313eb8bebf9616b9c24","blockSize":4194304,"blocks":["2fa880edd74153a393bab30ffb49669cb2d817dcc76fa313eb8bebf9616b9c24"]}},"CustomBuild.write.1.tlog":{"size":135,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"71fac8a943fa474bad8b07e4cbfd50e16214f6771d245c8648ce40ba36a49db8","blockSize":4194304,"blocks":["71fac8a943fa474bad8b07e4cbfd50e16214f6771d245c8648ce40ba36a49db8"]}},"copy.lastbuildstate":{"size":183,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"1d43546aec834e786961c4a9775b28540d1b9c78e28749ea90874cc61d4219d2","blockSize":4194304,"blocks":["1d43546aec834e786961c4a9775b28540d1b9c78e28749ea90874cc61d4219d2"]}}}}}}}},"binding.node":{"size":141824,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"2910b7468c283bb59a9711d4c9482d0310390a6b41191b2f5e8a37ca2ef7cfca","blockSize":4194304,"blocks":["2910b7468c283bb59a9711d4c9482d0310390a6b41191b2f5e8a37ca2ef7cfca"]}}}},"binding.vcxproj":{"size":12888,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"40f45a610997002381ba01fb6853ef035f019d97ee5608e2972873fa0b12f51e","blockSize":4194304,"blocks":["40f45a610997002381ba01fb6853ef035f019d97ee5608e2972873fa0b12f51e"]}},"binding.vcxproj.filters":{"size":3736,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"11a1a392bdaa3475feb6e78e194cad524ab2e19e13f7d693144118f13a4275d1","blockSize":4194304,"blocks":["11a1a392bdaa3475feb6e78e194cad524ab2e19e13f7d693144118f13a4275d1"]}},"copy.vcxproj":{"size":12134,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"03a801f290ac7d4a9e52334c411a2eded9075aed403e50c2c154b59fc77f00f6","blockSize":4194304,"blocks":["03a801f290ac7d4a9e52334c411a2eded9075aed403e50c2c154b59fc77f00f6"]}},"copy.vcxproj.filters":{"size":4583,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"756e5876da2559b84702aa0a4326399755ea9d34afd27affa6f3db807a8acad7","blockSize":4194304,"blocks":["756e5876da2559b84702aa0a4326399755ea9d34afd27affa6f3db807a8acad7"]}}}},"fontsMac.mm":{"size":761,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"0a1a83b87b2bc325695d6c611e35b476588c9ce14566081dba296095cbea4c20","blockSize":4194304,"blocks":["0a1a83b87b2bc325695d6c611e35b476588c9ce14566081dba296095cbea4c20"]}},"index.js":{"size":115,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"b44255cd3525bf6596a184d6d4db62c54be16826ac35c9384ead5f6a815a1b47","blockSize":4194304,"blocks":["b44255cd3525bf6596a184d6d4db62c54be16826ac35c9384ead5f6a815a1b47"]}},"package.json":{"size":339,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"be584f0765f4c2e00a3f86413f7c2be1c10668de047d579d7b7ed8e9aea1a7e3","blockSize":4194304,"blocks":["be584f0765f4c2e00a3f86413f7c2be1c10668de047d579d7b7ed8e9aea1a7e3"]}},"binding.node":{"size":141824,"unpacked":true,"integrity":{"algorithm":"SHA256","hash":"2910b7468c283bb59a9711d4c9482d0310390a6b41191b2f5e8a37ca2ef7cfca","blockSize":4194304,"blocks":["2910b7468c283bb59a9711d4c9482d0310390a6b41191b2f5e8a37ca2ef7cfca"]}}}},"node-addon-api":{"files":{"LICENSE.md":{"size":1150,"offset":"53668","integrity":{"algorithm":"SHA256","hash":"89024017b88a9f2b763f79b941a4f2db3b4428edfcacdc0b23866b2da633ad0c","blockSize":4194304,"blocks":["89024017b88a9f2b763f79b941a4f2db3b4428edfcacdc0b23866b2da633ad0c"]}},"common.gypi":{"size":797,"offset":"54818","integrity":{"algorithm":"SHA256","hash":"d1f38562aa923e16c81512df718e49e308ff6b407ae7ed8a90637603ffe70c34","blockSize":4194304,"blocks":["d1f38562aa923e16c81512df718e49e308ff6b407ae7ed8a90637603ffe70c34"]}},"except.gypi":{"size":560,"offset":"55615","integrity":{"algorithm":"SHA256","hash":"ddbc09f5b66fe24dd898fbe659085a6ff72e9575025004fc3762271dba781e8b","blockSize":4194304,"blocks":["ddbc09f5b66fe24dd898fbe659085a6ff72e9575025004fc3762271dba781e8b"]}},"index.js":{"size":435,"offset":"56175","integrity":{"algorithm":"SHA256","hash":"afbfa96b5c40c643828f212610129d2911095eff1e86e63ccfe81658d6fe61d9","blockSize":4194304,"blocks":["afbfa96b5c40c643828f212610129d2911095eff1e86e63ccfe81658d6fe61d9"]}},"napi-inl.deprecated.h":{"size":6323,"offset":"56610","integrity":{"algorithm":"SHA256","hash":"b66ed1e565e735bbfabcc2f72d466ab7c5414d3ef8851d1af440b81be7f2375c","blockSize":4194304,"blocks":["b66ed1e565e735bbfabcc2f72d466ab7c5414d3ef8851d1af440b81be7f2375c"]}},"napi-inl.h":{"size":233003,"offset":"62933","integrity":{"algorithm":"SHA256","hash":"ca32e9060f359983937904fba935681488d38c1ec0be483586bbf6e5a0a0991d","blockSize":4194304,"blocks":["ca32e9060f359983937904fba935681488d38c1ec0be483586bbf6e5a0a0991d"]}},"napi.h":{"size":119287,"offset":"295936","integrity":{"algorithm":"SHA256","hash":"c816220c48264108d7eace38e9e32b9bccf6488720ac34759c96ae97ef375b1d","blockSize":4194304,"blocks":["c816220c48264108d7eace38e9e32b9bccf6488720ac34759c96ae97ef375b1d"]}},"node_addon_api.gyp":{"size":1097,"offset":"415223","integrity":{"algorithm":"SHA256","hash":"6d34b9bae562103233f650d8a041edc0ca6b44d90898cd232b38745adfb7edfc","blockSize":4194304,"blocks":["6d34b9bae562103233f650d8a041edc0ca6b44d90898cd232b38745adfb7edfc"]}},"node_api.gyp":{"size":132,"offset":"416320","integrity":{"algorithm":"SHA256","hash":"3cd2c44fb0974f016376b676d46bbebbca7c89d4383b09ece30e4cb4122a1499","blockSize":4194304,"blocks":["3cd2c44fb0974f016376b676d46bbebbca7c89d4383b09ece30e4cb4122a1499"]}},"noexcept.gypi":{"size":649,"offset":"416452","integrity":{"algorithm":"SHA256","hash":"0e815179b5d2697e0acdc8f3c3a5d0a1ed050d48a295ee000454cf5352357f66","blockSize":4194304,"blocks":["0e815179b5d2697e0acdc8f3c3a5d0a1ed050d48a295ee000454cf5352357f66"]}},"nothing.c":{"size":0,"offset":"417101","integrity":{"algorithm":"SHA256","hash":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","blockSize":4194304,"blocks":["e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"]}},"package-support.json":{"size":467,"offset":"417101","integrity":{"algorithm":"SHA256","hash":"b1cb7da23cca1681c7392a3c889eb0cc4916c53d2d7692d4b654ae751f3442f3","blockSize":4194304,"blocks":["b1cb7da23cca1681c7392a3c889eb0cc4916c53d2d7692d4b654ae751f3442f3"]}},"package.json":{"size":783,"offset":"417568","integrity":{"algorithm":"SHA256","hash":"f1fb2d5a89dd4424741f59e4dac8e7dd76102fe756bbf59f2926307395900052","blockSize":4194304,"blocks":["f1fb2d5a89dd4424741f59e4dac8e7dd76102fe756bbf59f2926307395900052"]}},"tools":{"files":{"README.md":{"size":3217,"offset":"418351","integrity":{"algorithm":"SHA256","hash":"65e9ea918538f453166b10a1d609cf44cec3d2d01f23fecb5265fb3a4bf303d5","blockSize":4194304,"blocks":["65e9ea918538f453166b10a1d609cf44cec3d2d01f23fecb5265fb3a4bf303d5"]}},"check-napi.js":{"size":3176,"offset":"421568","integrity":{"algorithm":"SHA256","hash":"9e1207808023cd998fe9e377aefbe77d6c59fa129f94e6acbf24907149d8c11e","blockSize":4194304,"blocks":["9e1207808023cd998fe9e377aefbe77d6c59fa129f94e6acbf24907149d8c11e"]}},"clang-format.js":{"size":2002,"offset":"424744","integrity":{"algorithm":"SHA256","hash":"3e0b5e6d6f1a0c5ed106115871d0fd48f37bac5554effdacaf8d2439f4ed0c65","blockSize":4194304,"blocks":["3e0b5e6d6f1a0c5ed106115871d0fd48f37bac5554effdacaf8d2439f4ed0c65"]}},"conversion.js":{"size":15001,"offset":"426746","integrity":{"algorithm":"SHA256","hash":"9f375b0f9d9ec93c2ded07470478b3f523a2661c6ca707c28b0e9dae15009002","blockSize":4194304,"blocks":["9f375b0f9d9ec93c2ded07470478b3f523a2661c6ca707c28b0e9dae15009002"]}}}}}}}},"main.js":{"size":12758,"offset":"441747","integrity":{"algorithm":"SHA256","hash":"28df26f0f671c2d84e1f49c51c4f5d9b6014c2f2e0170d3015a9f04fcc2cec32","blockSize":4194304,"blocks":["28df26f0f671c2d84e1f49c51c4f5d9b6014c2f2e0170d3015a9f04fcc2cec32"]}},"package.json":{"size":304,"offset":"454505","integrity":{"algorithm":"SHA256","hash":"38b79586fb97867f7a77b3517189adaea72feb171f8b38b650c31bbff88a57c7","blockSize":4194304,"blocks":["38b79586fb97867f7a77b3517189adaea72feb171f8b38b650c31bbff88a57c7"]}}}}Copyright (c) 2019-2022 Electron contributors 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. "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getElectronBinding = void 0; const getElectronBinding = (name) => { if (process._linkedBinding) { return process._linkedBinding('electron_common_' + name); } else if (process.electronBinding) { return process.electronBinding(name); } else { return null; } }; exports.getElectronBinding = getElectronBinding; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; var _a, _b; Object.defineProperty(exports, "__esModule", { value: true }); exports.browserModuleNames = exports.commonModuleNames = void 0; const get_electron_binding_1 = require("./get-electron-binding"); exports.commonModuleNames = [ 'clipboard', 'nativeImage', 'shell', ]; exports.browserModuleNames = [ 'app', 'autoUpdater', 'BaseWindow', 'BrowserView', 'BrowserWindow', 'contentTracing', 'crashReporter', 'dialog', 'globalShortcut', 'ipcMain', 'inAppPurchase', 'Menu', 'MenuItem', 'nativeTheme', 'net', 'netLog', 'MessageChannelMain', 'Notification', 'powerMonitor', 'powerSaveBlocker', 'protocol', 'pushNotifications', 'safeStorage', 'screen', 'session', 'ShareMenu', 'systemPreferences', 'TopLevelWindow', 'TouchBar', 'Tray', 'utilityProcess', 'View', 'webContents', 'WebContentsView', 'webFrameMain', ].concat(exports.commonModuleNames); const features = get_electron_binding_1.getElectronBinding('features'); if (((_a = features === null || features === void 0 ? void 0 : features.isDesktopCapturerEnabled) === null || _a === void 0 ? void 0 : _a.call(features)) !== false) { exports.browserModuleNames.push('desktopCapturer'); } if (((_b = features === null || features === void 0 ? void 0 : features.isViewApiEnabled) === null || _b === void 0 ? void 0 : _b.call(features)) !== false) { exports.browserModuleNames.push('ImageView'); } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.deserialize = exports.serialize = exports.isSerializableObject = exports.isPromise = void 0; const electron_1 = require("electron"); function isPromise(val) { return (val && val.then && val.then instanceof Function && val.constructor && val.constructor.reject && val.constructor.reject instanceof Function && val.constructor.resolve && val.constructor.resolve instanceof Function); } exports.isPromise = isPromise; const serializableTypes = [ Boolean, Number, String, Date, Error, RegExp, ArrayBuffer ]; // https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#Supported_types function isSerializableObject(value) { return value === null || ArrayBuffer.isView(value) || serializableTypes.some(type => value instanceof type); } exports.isSerializableObject = isSerializableObject; const objectMap = function (source, mapper) { const sourceEntries = Object.entries(source); const targetEntries = sourceEntries.map(([key, val]) => [key, mapper(val)]); return Object.fromEntries(targetEntries); }; function serializeNativeImage(image) { const representations = []; const scaleFactors = image.getScaleFactors(); // Use Buffer when there's only one representation for better perf. // This avoids compressing to/from PNG where it's not necessary to // ensure uniqueness of dataURLs (since there's only one). if (scaleFactors.length === 1) { const scaleFactor = scaleFactors[0]; const size = image.getSize(scaleFactor); const buffer = image.toBitmap({ scaleFactor }); representations.push({ scaleFactor, size, buffer }); } else { // Construct from dataURLs to ensure that they are not lost in creation. for (const scaleFactor of scaleFactors) { const size = image.getSize(scaleFactor); const dataURL = image.toDataURL({ scaleFactor }); representations.push({ scaleFactor, size, dataURL }); } } return { __ELECTRON_SERIALIZED_NativeImage__: true, representations }; } function deserializeNativeImage(value) { const image = electron_1.nativeImage.createEmpty(); // Use Buffer when there's only one representation for better perf. // This avoids compressing to/from PNG where it's not necessary to // ensure uniqueness of dataURLs (since there's only one). if (value.representations.length === 1) { const { buffer, size, scaleFactor } = value.representations[0]; const { width, height } = size; image.addRepresentation({ buffer, scaleFactor, width, height }); } else { // Construct from dataURLs to ensure that they are not lost in creation. for (const rep of value.representations) { const { dataURL, size, scaleFactor } = rep; const { width, height } = size; image.addRepresentation({ dataURL, scaleFactor, width, height }); } } return image; } function serialize(value) { if (value && value.constructor && value.constructor.name === 'NativeImage') { return serializeNativeImage(value); } if (Array.isArray(value)) { return value.map(serialize); } else if (isSerializableObject(value)) { return value; } else if (value instanceof Object) { return objectMap(value, serialize); } else { return value; } } exports.serialize = serialize; function deserialize(value) { if (value && value.__ELECTRON_SERIALIZED_NativeImage__) { return deserializeNativeImage(value); } else if (Array.isArray(value)) { return value.map(deserialize); } else if (isSerializableObject(value)) { return value; } else if (value instanceof Object) { return objectMap(value, deserialize); } else { return value; } } exports.deserialize = deserialize; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.enable = exports.isInitialized = exports.initialize = void 0; var server_1 = require("./server"); Object.defineProperty(exports, "initialize", { enumerable: true, get: function () { return server_1.initialize; } }); Object.defineProperty(exports, "isInitialized", { enumerable: true, get: function () { return server_1.isInitialized; } }); Object.defineProperty(exports, "enable", { enumerable: true, get: function () { return server_1.enable; } }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const getOwnerKey = (webContents, contextId) => { return `${webContents.id}-${contextId}`; }; class ObjectsRegistry { constructor() { this.nextId = 0; // Stores all objects by ref-counting. // (id) => {object, count} this.storage = {}; // Stores the IDs + refCounts of objects referenced by WebContents. // (ownerKey) => { id: refCount } this.owners = {}; this.electronIds = new WeakMap(); } // Register a new object and return its assigned ID. If the object is already // registered then the already assigned ID would be returned. add(webContents, contextId, obj) { // Get or assign an ID to the object. const id = this.saveToStorage(obj); // Add object to the set of referenced objects. const ownerKey = getOwnerKey(webContents, contextId); let owner = this.owners[ownerKey]; if (!owner) { owner = this.owners[ownerKey] = new Map(); this.registerDeleteListener(webContents, contextId); } if (!owner.has(id)) { owner.set(id, 0); // Increase reference count if not referenced before. this.storage[id].count++; } owner.set(id, owner.get(id) + 1); return id; } // Get an object according to its ID. get(id) { const pointer = this.storage[id]; if (pointer != null) return pointer.object; } // Dereference an object according to its ID. // Note that an object may be double-freed (cleared when page is reloaded, and // then garbage collected in old page). remove(webContents, contextId, id) { const ownerKey = getOwnerKey(webContents, contextId); const owner = this.owners[ownerKey]; if (owner && owner.has(id)) { const newRefCount = owner.get(id) - 1; // Only completely remove if the number of references GCed in the // renderer is the same as the number of references we sent them if (newRefCount <= 0) { // Remove the reference in owner. owner.delete(id); // Dereference from the storage. this.dereference(id); } else { owner.set(id, newRefCount); } } } // Clear all references to objects refrenced by the WebContents. clear(webContents, contextId) { const ownerKey = getOwnerKey(webContents, contextId); const owner = this.owners[ownerKey]; if (!owner) return; for (const id of owner.keys()) this.dereference(id); delete this.owners[ownerKey]; } // Saves the object into storage and assigns an ID for it. saveToStorage(object) { let id = this.electronIds.get(object); if (!id) { id = ++this.nextId; this.storage[id] = { count: 0, object: object }; this.electronIds.set(object, id); } return id; } // Dereference the object from store. dereference(id) { const pointer = this.storage[id]; if (pointer == null) { return; } pointer.count -= 1; if (pointer.count === 0) { this.electronIds.delete(pointer.object); delete this.storage[id]; } } // Clear the storage when renderer process is destroyed. registerDeleteListener(webContents, contextId) { // contextId => ${processHostId}-${contextCount} const processHostId = contextId.split('-')[0]; const listener = (_, deletedProcessHostId) => { if (deletedProcessHostId && deletedProcessHostId.toString() === processHostId) { webContents.removeListener('render-view-deleted', listener); this.clear(webContents, contextId); } }; // Note that the "render-view-deleted" event may not be emitted on time when // the renderer process get destroyed because of navigation, we rely on the // renderer process to send "ELECTRON_BROWSER_CONTEXT_RELEASE" message to // guard this situation. webContents.on('render-view-deleted', listener); } } exports.default = new ObjectsRegistry(); "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.initialize = exports.isInitialized = exports.enable = exports.isRemoteModuleEnabled = void 0; const events_1 = require("events"); const objects_registry_1 = __importDefault(require("./objects-registry")); const type_utils_1 = require("../common/type-utils"); const electron_1 = require("electron"); const get_electron_binding_1 = require("../common/get-electron-binding"); const { Promise } = global; const v8Util = get_electron_binding_1.getElectronBinding('v8_util'); const hasWebPrefsRemoteModuleAPI = (() => { var _a, _b; const electronVersion = Number((_b = (_a = process.versions.electron) === null || _a === void 0 ? void 0 : _a.split(".")) === null || _b === void 0 ? void 0 : _b[0]); return Number.isNaN(electronVersion) || electronVersion < 14; })(); // The internal properties of Function. const FUNCTION_PROPERTIES = [ 'length', 'name', 'arguments', 'caller', 'prototype' ]; // The remote functions in renderer processes. const rendererFunctionCache = new Map(); // eslint-disable-next-line no-undef const finalizationRegistry = new FinalizationRegistry((fi) => { const mapKey = fi.id[0] + '~' + fi.id[1]; const ref = rendererFunctionCache.get(mapKey); if (ref !== undefined && ref.deref() === undefined) { rendererFunctionCache.delete(mapKey); if (!fi.webContents.isDestroyed()) { try { fi.webContents.sendToFrame(fi.frameId, "REMOTE_RENDERER_RELEASE_CALLBACK" /* RENDERER_RELEASE_CALLBACK */, fi.id[0], fi.id[1]); } catch (error) { console.warn(`sendToFrame() failed: ${error}`); } } } }); function getCachedRendererFunction(id) { const mapKey = id[0] + '~' + id[1]; const ref = rendererFunctionCache.get(mapKey); if (ref !== undefined) { const deref = ref.deref(); if (deref !== undefined) return deref; } } function setCachedRendererFunction(id, wc, frameId, value) { // eslint-disable-next-line no-undef const wr = new WeakRef(value); const mapKey = id[0] + '~' + id[1]; rendererFunctionCache.set(mapKey, wr); finalizationRegistry.register(value, { id, webContents: wc, frameId }); return value; } const locationInfo = new WeakMap(); // Return the description of object's members: const getObjectMembers = function (object) { let names = Object.getOwnPropertyNames(object); // For Function, we should not override following properties even though they // are "own" properties. if (typeof object === 'function') { names = names.filter((name) => { return !FUNCTION_PROPERTIES.includes(name); }); } // Map properties to descriptors. return names.map((name) => { const descriptor = Object.getOwnPropertyDescriptor(object, name); let type; let writable = false; if (descriptor.get === undefined && typeof object[name] === 'function') { type = 'method'; } else { if (descriptor.set || descriptor.writable) writable = true; type = 'get'; } return { name, enumerable: descriptor.enumerable, writable, type }; }); }; // Return the description of object's prototype. const getObjectPrototype = function (object) { const proto = Object.getPrototypeOf(object); if (proto === null || proto === Object.prototype) return null; return { members: getObjectMembers(proto), proto: getObjectPrototype(proto) }; }; // Convert a real value into meta data. const valueToMeta = function (sender, contextId, value, optimizeSimpleObject = false) { // Determine the type of value. let type; switch (typeof value) { case 'object': // Recognize certain types of objects. if (value instanceof Buffer) { type = 'buffer'; } else if (value && value.constructor && value.constructor.name === 'NativeImage') { type = 'nativeimage'; } else if (Array.isArray(value)) { type = 'array'; } else if (value instanceof Error) { type = 'error'; } else if (type_utils_1.isSerializableObject(value)) { type = 'value'; } else if (type_utils_1.isPromise(value)) { type = 'promise'; } else if (Object.prototype.hasOwnProperty.call(value, 'callee') && value.length != null) { // Treat the arguments object as array. type = 'array'; } else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) { // Treat simple objects as value. type = 'value'; } else { type = 'object'; } break; case 'function': type = 'function'; break; default: type = 'value'; break; } // Fill the meta object according to value's type. if (type === 'array') { return { type, members: value.map((el) => valueToMeta(sender, contextId, el, optimizeSimpleObject)) }; } else if (type === 'nativeimage') { return { type, value: type_utils_1.serialize(value) }; } else if (type === 'object' || type === 'function') { return { type, name: value.constructor ? value.constructor.name : '', // Reference the original value if it's an object, because when it's // passed to renderer we would assume the renderer keeps a reference of // it. id: objects_registry_1.default.add(sender, contextId, value), members: getObjectMembers(value), proto: getObjectPrototype(value) }; } else if (type === 'buffer') { return { type, value }; } else if (type === 'promise') { // Add default handler to prevent unhandled rejections in main process // Instead they should appear in the renderer process value.then(function () { }, function () { }); return { type, then: valueToMeta(sender, contextId, function (onFulfilled, onRejected) { value.then(onFulfilled, onRejected); }) }; } else if (type === 'error') { return { type, value, members: Object.keys(value).map(name => ({ name, value: valueToMeta(sender, contextId, value[name]) })) }; } else { return { type: 'value', value }; } }; const throwRPCError = function (message) { const error = new Error(message); error.code = 'EBADRPC'; error.errno = -72; throw error; }; const removeRemoteListenersAndLogWarning = (sender, callIntoRenderer) => { const location = locationInfo.get(callIntoRenderer); let message = 'Attempting to call a function in a renderer window that has been closed or released.' + `\nFunction provided here: ${location}`; if (sender instanceof events_1.EventEmitter) { const remoteEvents = sender.eventNames().filter((eventName) => { return sender.listeners(eventName).includes(callIntoRenderer); }); if (remoteEvents.length > 0) { message += `\nRemote event names: ${remoteEvents.join(', ')}`; remoteEvents.forEach((eventName) => { sender.removeListener(eventName, callIntoRenderer); }); } } console.warn(message); }; const fakeConstructor = (constructor, name) => new Proxy(Object, { get(target, prop, receiver) { if (prop === 'name') { return name; } else { return Reflect.get(target, prop, receiver); } } }); // Convert array of meta data from renderer into array of real values. const unwrapArgs = function (sender, frameId, contextId, args) { const metaToValue = function (meta) { switch (meta.type) { case 'nativeimage': return type_utils_1.deserialize(meta.value); case 'value': return meta.value; case 'remote-object': return objects_registry_1.default.get(meta.id); case 'array': return unwrapArgs(sender, frameId, contextId, meta.value); case 'buffer': return Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength); case 'promise': return Promise.resolve({ then: metaToValue(meta.then) }); case 'object': { const ret = meta.name !== 'Object' ? Object.create({ constructor: fakeConstructor(Object, meta.name) }) : {}; for (const { name, value } of meta.members) { ret[name] = metaToValue(value); } return ret; } case 'function-with-return-value': { const returnValue = metaToValue(meta.value); return function () { return returnValue; }; } case 'function': { // Merge contextId and meta.id, since meta.id can be the same in // different webContents. const objectId = [contextId, meta.id]; // Cache the callbacks in renderer. const cachedFunction = getCachedRendererFunction(objectId); if (cachedFunction !== undefined) { return cachedFunction; } const callIntoRenderer = function (...args) { let succeed = false; if (!sender.isDestroyed()) { try { succeed = sender.sendToFrame(frameId, "REMOTE_RENDERER_CALLBACK" /* RENDERER_CALLBACK */, contextId, meta.id, valueToMeta(sender, contextId, args)) !== false; } catch (error) { console.warn(`sendToFrame() failed: ${error}`); } } if (!succeed) { removeRemoteListenersAndLogWarning(this, callIntoRenderer); } }; locationInfo.set(callIntoRenderer, meta.location); Object.defineProperty(callIntoRenderer, 'length', { value: meta.length }); setCachedRendererFunction(objectId, sender, frameId, callIntoRenderer); return callIntoRenderer; } default: throw new TypeError(`Unknown type: ${meta.type}`); } }; return args.map(metaToValue); }; const isRemoteModuleEnabledImpl = function (contents) { const webPreferences = contents.getLastWebPreferences() || {}; return webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : false; }; const isRemoteModuleEnabledCache = new WeakMap(); const isRemoteModuleEnabled = function (contents) { if (hasWebPrefsRemoteModuleAPI && !isRemoteModuleEnabledCache.has(contents)) { isRemoteModuleEnabledCache.set(contents, isRemoteModuleEnabledImpl(contents)); } return isRemoteModuleEnabledCache.get(contents); }; exports.isRemoteModuleEnabled = isRemoteModuleEnabled; function enable(contents) { isRemoteModuleEnabledCache.set(contents, true); } exports.enable = enable; const handleRemoteCommand = function (channel, handler) { electron_1.ipcMain.on(channel, (event, contextId, ...args) => { let returnValue; if (!exports.isRemoteModuleEnabled(event.sender)) { event.returnValue = { type: 'exception', value: valueToMeta(event.sender, contextId, new Error('@electron/remote is disabled for this WebContents. Call require("@electron/remote/main").enable(webContents) to enable it.')) }; return; } try { returnValue = handler(event, contextId, ...args); } catch (error) { returnValue = { type: 'exception', value: valueToMeta(event.sender, contextId, error), }; } if (returnValue !== undefined) { event.returnValue = returnValue; } }); }; const emitCustomEvent = function (contents, eventName, ...args) { const event = { sender: contents, returnValue: undefined, defaultPrevented: false }; electron_1.app.emit(eventName, event, contents, ...args); contents.emit(eventName, event, ...args); return event; }; const logStack = function (contents, code, stack) { if (stack) { console.warn(`WebContents (${contents.id}): ${code}`, stack); } }; let initialized = false; function isInitialized() { return initialized; } exports.isInitialized = isInitialized; function initialize() { if (initialized) throw new Error('@electron/remote has already been initialized'); initialized = true; handleRemoteCommand("REMOTE_BROWSER_WRONG_CONTEXT_ERROR" /* BROWSER_WRONG_CONTEXT_ERROR */, function (event, contextId, passedContextId, id) { const objectId = [passedContextId, id]; const cachedFunction = getCachedRendererFunction(objectId); if (cachedFunction === undefined) { // Do nothing if the error has already been reported before. return; } removeRemoteListenersAndLogWarning(event.sender, cachedFunction); }); handleRemoteCommand("REMOTE_BROWSER_REQUIRE" /* BROWSER_REQUIRE */, function (event, contextId, moduleName, stack) { logStack(event.sender, `remote.require('${moduleName}')`, stack); const customEvent = emitCustomEvent(event.sender, 'remote-require', moduleName); if (customEvent.returnValue === undefined) { if (customEvent.defaultPrevented) { throw new Error(`Blocked remote.require('${moduleName}')`); } else { // electron < 28 if (process.mainModule) { customEvent.returnValue = process.mainModule.require(moduleName); } else { // electron >= 28 let mainModule = module; while (mainModule.parent) { mainModule = mainModule.parent; } customEvent.returnValue = mainModule.require(moduleName); } } } return valueToMeta(event.sender, contextId, customEvent.returnValue); }); handleRemoteCommand("REMOTE_BROWSER_GET_BUILTIN" /* BROWSER_GET_BUILTIN */, function (event, contextId, moduleName, stack) { logStack(event.sender, `remote.getBuiltin('${moduleName}')`, stack); const customEvent = emitCustomEvent(event.sender, 'remote-get-builtin', moduleName); if (customEvent.returnValue === undefined) { if (customEvent.defaultPrevented) { throw new Error(`Blocked remote.getBuiltin('${moduleName}')`); } else { customEvent.returnValue = require('electron')[moduleName]; } } return valueToMeta(event.sender, contextId, customEvent.returnValue); }); handleRemoteCommand("REMOTE_BROWSER_GET_GLOBAL" /* BROWSER_GET_GLOBAL */, function (event, contextId, globalName, stack) { logStack(event.sender, `remote.getGlobal('${globalName}')`, stack); const customEvent = emitCustomEvent(event.sender, 'remote-get-global', globalName); if (customEvent.returnValue === undefined) { if (customEvent.defaultPrevented) { throw new Error(`Blocked remote.getGlobal('${globalName}')`); } else { customEvent.returnValue = global[globalName]; } } return valueToMeta(event.sender, contextId, customEvent.returnValue); }); handleRemoteCommand("REMOTE_BROWSER_GET_CURRENT_WINDOW" /* BROWSER_GET_CURRENT_WINDOW */, function (event, contextId, stack) { logStack(event.sender, 'remote.getCurrentWindow()', stack); const customEvent = emitCustomEvent(event.sender, 'remote-get-current-window'); if (customEvent.returnValue === undefined) { if (customEvent.defaultPrevented) { throw new Error('Blocked remote.getCurrentWindow()'); } else { customEvent.returnValue = event.sender.getOwnerBrowserWindow(); } } return valueToMeta(event.sender, contextId, customEvent.returnValue); }); handleRemoteCommand("REMOTE_BROWSER_GET_CURRENT_WEB_CONTENTS" /* BROWSER_GET_CURRENT_WEB_CONTENTS */, function (event, contextId, stack) { logStack(event.sender, 'remote.getCurrentWebContents()', stack); const customEvent = emitCustomEvent(event.sender, 'remote-get-current-web-contents'); if (customEvent.returnValue === undefined) { if (customEvent.defaultPrevented) { throw new Error('Blocked remote.getCurrentWebContents()'); } else { customEvent.returnValue = event.sender; } } return valueToMeta(event.sender, contextId, customEvent.returnValue); }); handleRemoteCommand("REMOTE_BROWSER_CONSTRUCTOR" /* BROWSER_CONSTRUCTOR */, function (event, contextId, id, args) { args = unwrapArgs(event.sender, event.frameId, contextId, args); const constructor = objects_registry_1.default.get(id); if (constructor == null) { throwRPCError(`Cannot call constructor on missing remote object ${id}`); } return valueToMeta(event.sender, contextId, new constructor(...args)); }); handleRemoteCommand("REMOTE_BROWSER_FUNCTION_CALL" /* BROWSER_FUNCTION_CALL */, function (event, contextId, id, args) { args = unwrapArgs(event.sender, event.frameId, contextId, args); const func = objects_registry_1.default.get(id); if (func == null) { throwRPCError(`Cannot call function on missing remote object ${id}`); } try { return valueToMeta(event.sender, contextId, func(...args), true); } catch (error) { const err = new Error(`Could not call remote function '${func.name || "anonymous"}'. Check that the function signature is correct. Underlying error: ${error}\n` + (error instanceof Error ? `Underlying stack: ${error.stack}\n` : "")); err.cause = error; throw err; } }); handleRemoteCommand("REMOTE_BROWSER_MEMBER_CONSTRUCTOR" /* BROWSER_MEMBER_CONSTRUCTOR */, function (event, contextId, id, method, args) { args = unwrapArgs(event.sender, event.frameId, contextId, args); const object = objects_registry_1.default.get(id); if (object == null) { throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`); } return valueToMeta(event.sender, contextId, new object[method](...args)); }); handleRemoteCommand("REMOTE_BROWSER_MEMBER_CALL" /* BROWSER_MEMBER_CALL */, function (event, contextId, id, method, args) { args = unwrapArgs(event.sender, event.frameId, contextId, args); const object = objects_registry_1.default.get(id); if (object == null) { throwRPCError(`Cannot call method '${method}' on missing remote object ${id}`); } try { return valueToMeta(event.sender, contextId, object[method](...args), true); } catch (error) { const err = new Error(`Could not call remote method '${method}'. Check that the method signature is correct. Underlying error: ${error}` + (error instanceof Error ? `Underlying stack: ${error.stack}\n` : "")); err.cause = error; throw err; } }); handleRemoteCommand("REMOTE_BROWSER_MEMBER_SET" /* BROWSER_MEMBER_SET */, function (event, contextId, id, name, args) { args = unwrapArgs(event.sender, event.frameId, contextId, args); const obj = objects_registry_1.default.get(id); if (obj == null) { throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`); } obj[name] = args[0]; return null; }); handleRemoteCommand("REMOTE_BROWSER_MEMBER_GET" /* BROWSER_MEMBER_GET */, function (event, contextId, id, name) { const obj = objects_registry_1.default.get(id); if (obj == null) { throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`); } return valueToMeta(event.sender, contextId, obj[name]); }); handleRemoteCommand("REMOTE_BROWSER_DEREFERENCE" /* BROWSER_DEREFERENCE */, function (event, contextId, id) { objects_registry_1.default.remove(event.sender, contextId, id); }); handleRemoteCommand("REMOTE_BROWSER_CONTEXT_RELEASE" /* BROWSER_CONTEXT_RELEASE */, (event, contextId) => { objects_registry_1.default.clear(event.sender, contextId); return null; }); } exports.initialize = initialize; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CallbacksRegistry = void 0; class CallbacksRegistry { constructor() { this.nextId = 0; this.callbacks = {}; this.callbackIds = new WeakMap(); this.locationInfo = new WeakMap(); } add(callback) { // The callback is already added. let id = this.callbackIds.get(callback); if (id != null) return id; id = this.nextId += 1; this.callbacks[id] = callback; this.callbackIds.set(callback, id); // Capture the location of the function and put it in the ID string, // so that release errors can be tracked down easily. const regexp = /at (.*)/gi; const stackString = (new Error()).stack; if (!stackString) return id; let filenameAndLine; let match; while ((match = regexp.exec(stackString)) !== null) { const location = match[1]; if (location.includes('(native)')) continue; if (location.includes('()')) continue; if (location.includes('callbacks-registry.js')) continue; if (location.includes('remote.js')) continue; if (location.includes('@electron/remote/dist')) continue; const ref = /([^/^)]*)\)?$/gi.exec(location); if (ref) filenameAndLine = ref[1]; break; } this.locationInfo.set(callback, filenameAndLine); return id; } get(id) { return this.callbacks[id] || function () { }; } getLocation(callback) { return this.locationInfo.get(callback); } apply(id, ...args) { return this.get(id).apply(global, ...args); } remove(id) { const callback = this.callbacks[id]; if (callback) { this.callbackIds.delete(callback); delete this.callbacks[id]; } } } exports.CallbacksRegistry = CallbacksRegistry; "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); if (process.type === 'browser') throw new Error(`"@electron/remote" cannot be required in the browser process. Instead require("@electron/remote/main").`); __exportStar(require("./remote"), exports); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createFunctionWithReturnValue = exports.getGlobal = exports.getCurrentWebContents = exports.getCurrentWindow = exports.getBuiltin = void 0; const callbacks_registry_1 = require("./callbacks-registry"); const type_utils_1 = require("../common/type-utils"); const electron_1 = require("electron"); const module_names_1 = require("../common/module-names"); const get_electron_binding_1 = require("../common/get-electron-binding"); const { Promise } = global; const callbacksRegistry = new callbacks_registry_1.CallbacksRegistry(); const remoteObjectCache = new Map(); const finalizationRegistry = new FinalizationRegistry((id) => { const ref = remoteObjectCache.get(id); if (ref !== undefined && ref.deref() === undefined) { remoteObjectCache.delete(id); electron_1.ipcRenderer.send("REMOTE_BROWSER_DEREFERENCE" /* BROWSER_DEREFERENCE */, contextId, id, 0); } }); const electronIds = new WeakMap(); const isReturnValue = new WeakSet(); function getCachedRemoteObject(id) { const ref = remoteObjectCache.get(id); if (ref !== undefined) { const deref = ref.deref(); if (deref !== undefined) return deref; } } function setCachedRemoteObject(id, value) { const wr = new WeakRef(value); remoteObjectCache.set(id, wr); finalizationRegistry.register(value, id); return value; } function getContextId() { const v8Util = get_electron_binding_1.getElectronBinding('v8_util'); if (v8Util) { return v8Util.getHiddenValue(global, 'contextId'); } else { throw new Error('Electron >=v13.0.0-beta.6 required to support sandboxed renderers'); } } // An unique ID that can represent current context. const contextId = process.contextId || getContextId(); // Notify the main process when current context is going to be released. // Note that when the renderer process is destroyed, the message may not be // sent, we also listen to the "render-view-deleted" event in the main process // to guard that situation. process.on('exit', () => { const command = "REMOTE_BROWSER_CONTEXT_RELEASE" /* BROWSER_CONTEXT_RELEASE */; electron_1.ipcRenderer.send(command, contextId); }); const IS_REMOTE_PROXY = Symbol('is-remote-proxy'); // Convert the arguments object into an array of meta data. function wrapArgs(args, visited = new Set()) { const valueToMeta = (value) => { // Check for circular reference. if (visited.has(value)) { return { type: 'value', value: null }; } if (value && value.constructor && value.constructor.name === 'NativeImage') { return { type: 'nativeimage', value: type_utils_1.serialize(value) }; } else if (Array.isArray(value)) { visited.add(value); const meta = { type: 'array', value: wrapArgs(value, visited) }; visited.delete(value); return meta; } else if (value instanceof Buffer) { return { type: 'buffer', value }; } else if (type_utils_1.isSerializableObject(value)) { return { type: 'value', value }; } else if (typeof value === 'object') { if (type_utils_1.isPromise(value)) { return { type: 'promise', then: valueToMeta(function (onFulfilled, onRejected) { value.then(onFulfilled, onRejected); }) }; } else if (electronIds.has(value)) { return { type: 'remote-object', id: electronIds.get(value) }; } const meta = { type: 'object', name: value.constructor ? value.constructor.name : '', members: [] }; visited.add(value); for (const prop in value) { // eslint-disable-line guard-for-in meta.members.push({ name: prop, value: valueToMeta(value[prop]) }); } visited.delete(value); return meta; } else if (typeof value === 'function' && isReturnValue.has(value)) { return { type: 'function-with-return-value', value: valueToMeta(value()) }; } else if (typeof value === 'function') { return { type: 'function', id: callbacksRegistry.add(value), location: callbacksRegistry.getLocation(value), length: value.length }; } else { return { type: 'value', value }; } }; return args.map(valueToMeta); } // Populate object's members from descriptors. // The |ref| will be kept referenced by |members|. // This matches |getObjectMemebers| in rpc-server. function setObjectMembers(ref, object, metaId, members) { if (!Array.isArray(members)) return; for (const member of members) { if (Object.prototype.hasOwnProperty.call(object, member.name)) continue; const descriptor = { enumerable: member.enumerable }; if (member.type === 'method') { const remoteMemberFunction = function (...args) { let command; if (this && this.constructor === remoteMemberFunction) { command = "REMOTE_BROWSER_MEMBER_CONSTRUCTOR" /* BROWSER_MEMBER_CONSTRUCTOR */; } else { command = "REMOTE_BROWSER_MEMBER_CALL" /* BROWSER_MEMBER_CALL */; } const ret = electron_1.ipcRenderer.sendSync(command, contextId, metaId, member.name, wrapArgs(args)); return metaToValue(ret); }; let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name); descriptor.get = () => { descriptorFunction.ref = ref; // The member should reference its object. return descriptorFunction; }; // Enable monkey-patch the method descriptor.set = (value) => { descriptorFunction = value; return value; }; descriptor.configurable = true; } else if (member.type === 'get') { descriptor.get = () => { const command = "REMOTE_BROWSER_MEMBER_GET" /* BROWSER_MEMBER_GET */; const meta = electron_1.ipcRenderer.sendSync(command, contextId, metaId, member.name); return metaToValue(meta); }; if (member.writable) { descriptor.set = (value) => { const args = wrapArgs([value]); const command = "REMOTE_BROWSER_MEMBER_SET" /* BROWSER_MEMBER_SET */; const meta = electron_1.ipcRenderer.sendSync(command, contextId, metaId, member.name, args); if (meta != null) metaToValue(meta); return value; }; } } Object.defineProperty(object, member.name, descriptor); } } // Populate object's prototype from descriptor. // This matches |getObjectPrototype| in rpc-server. function setObjectPrototype(ref, object, metaId, descriptor) { if (descriptor === null) return; const proto = {}; setObjectMembers(ref, proto, metaId, descriptor.members); setObjectPrototype(ref, proto, metaId, descriptor.proto); Object.setPrototypeOf(object, proto); } // Wrap function in Proxy for accessing remote properties function proxyFunctionProperties(remoteMemberFunction, metaId, name) { let loaded = false; // Lazily load function properties const loadRemoteProperties = () => { if (loaded) return; loaded = true; const command = "REMOTE_BROWSER_MEMBER_GET" /* BROWSER_MEMBER_GET */; const meta = electron_1.ipcRenderer.sendSync(command, contextId, metaId, name); setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members); }; return new Proxy(remoteMemberFunction, { set: (target, property, value) => { if (property !== 'ref') loadRemoteProperties(); target[property] = value; return true; }, get: (target, property) => { if (property === IS_REMOTE_PROXY) return true; if (!Object.prototype.hasOwnProperty.call(target, property)) loadRemoteProperties(); const value = target[property]; if (property === 'toString' && typeof value === 'function') { return value.bind(target); } return value; }, ownKeys: (target) => { loadRemoteProperties(); return Object.getOwnPropertyNames(target); }, getOwnPropertyDescriptor: (target, property) => { const descriptor = Object.getOwnPropertyDescriptor(target, property); if (descriptor) return descriptor; loadRemoteProperties(); return Object.getOwnPropertyDescriptor(target, property); } }); } // Convert meta data from browser into real value. function metaToValue(meta) { if (!meta) return {}; if (meta.type === 'value') { return meta.value; } else if (meta.type === 'array') { return meta.members.map((member) => metaToValue(member)); } else if (meta.type === 'nativeimage') { return type_utils_1.deserialize(meta.value); } else if (meta.type === 'buffer') { return Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength); } else if (meta.type === 'promise') { return Promise.resolve({ then: metaToValue(meta.then) }); } else if (meta.type === 'error') { return metaToError(meta); } else if (meta.type === 'exception') { if (meta.value.type === 'error') { throw metaToError(meta.value); } else { throw new Error(`Unexpected value type in exception: ${meta.value.type}`); } } else { let ret; if ('id' in meta) { const cached = getCachedRemoteObject(meta.id); if (cached !== undefined) { return cached; } } // A shadow class to represent the remote function object. if (meta.type === 'function') { const remoteFunction = function (...args) { let command; if (this && this.constructor === remoteFunction) { command = "REMOTE_BROWSER_CONSTRUCTOR" /* BROWSER_CONSTRUCTOR */; } else { command = "REMOTE_BROWSER_FUNCTION_CALL" /* BROWSER_FUNCTION_CALL */; } const obj = electron_1.ipcRenderer.sendSync(command, contextId, meta.id, wrapArgs(args)); return metaToValue(obj); }; ret = remoteFunction; } else { ret = {}; } setObjectMembers(ret, ret, meta.id, meta.members); setObjectPrototype(ret, ret, meta.id, meta.proto); if (ret.constructor && ret.constructor[IS_REMOTE_PROXY]) { Object.defineProperty(ret.constructor, 'name', { value: meta.name }); } // Track delegate obj's lifetime & tell browser to clean up when object is GCed. electronIds.set(ret, meta.id); setCachedRemoteObject(meta.id, ret); return ret; } } function metaToError(meta) { const obj = meta.value; for (const { name, value } of meta.members) { obj[name] = metaToValue(value); } return obj; } function hasSenderId(input) { return typeof input.senderId === "number"; } function handleMessage(channel, handler) { electron_1.ipcRenderer.on(channel, (event, passedContextId, id, ...args) => { if (hasSenderId(event)) { if (event.senderId !== 0 && event.senderId !== undefined) { console.error(`Message ${channel} sent by unexpected WebContents (${event.senderId})`); return; } } if (passedContextId === contextId) { handler(id, ...args); } else { // Message sent to an un-exist context, notify the error to main process. electron_1.ipcRenderer.send("REMOTE_BROWSER_WRONG_CONTEXT_ERROR" /* BROWSER_WRONG_CONTEXT_ERROR */, contextId, passedContextId, id); } }); } const enableStacks = process.argv.includes('--enable-api-filtering-logging'); function getCurrentStack() { const target = { stack: undefined }; if (enableStacks) { Error.captureStackTrace(target, getCurrentStack); } return target.stack; } // Browser calls a callback in renderer. handleMessage("REMOTE_RENDERER_CALLBACK" /* RENDERER_CALLBACK */, (id, args) => { callbacksRegistry.apply(id, metaToValue(args)); }); // A callback in browser is released. handleMessage("REMOTE_RENDERER_RELEASE_CALLBACK" /* RENDERER_RELEASE_CALLBACK */, (id) => { callbacksRegistry.remove(id); }); exports.require = (module) => { const command = "REMOTE_BROWSER_REQUIRE" /* BROWSER_REQUIRE */; const meta = electron_1.ipcRenderer.sendSync(command, contextId, module, getCurrentStack()); return metaToValue(meta); }; // Alias to remote.require('electron').xxx. function getBuiltin(module) { const command = "REMOTE_BROWSER_GET_BUILTIN" /* BROWSER_GET_BUILTIN */; const meta = electron_1.ipcRenderer.sendSync(command, contextId, module, getCurrentStack()); return metaToValue(meta); } exports.getBuiltin = getBuiltin; function getCurrentWindow() { const command = "REMOTE_BROWSER_GET_CURRENT_WINDOW" /* BROWSER_GET_CURRENT_WINDOW */; const meta = electron_1.ipcRenderer.sendSync(command, contextId, getCurrentStack()); return metaToValue(meta); } exports.getCurrentWindow = getCurrentWindow; // Get current WebContents object. function getCurrentWebContents() { const command = "REMOTE_BROWSER_GET_CURRENT_WEB_CONTENTS" /* BROWSER_GET_CURRENT_WEB_CONTENTS */; const meta = electron_1.ipcRenderer.sendSync(command, contextId, getCurrentStack()); return metaToValue(meta); } exports.getCurrentWebContents = getCurrentWebContents; // Get a global object in browser. function getGlobal(name) { const command = "REMOTE_BROWSER_GET_GLOBAL" /* BROWSER_GET_GLOBAL */; const meta = electron_1.ipcRenderer.sendSync(command, contextId, name, getCurrentStack()); return metaToValue(meta); } exports.getGlobal = getGlobal; // Get the process object in browser. Object.defineProperty(exports, 'process', { enumerable: true, get: () => exports.getGlobal('process') }); // Create a function that will return the specified value when called in browser. function createFunctionWithReturnValue(returnValue) { const func = () => returnValue; isReturnValue.add(func); return func; } exports.createFunctionWithReturnValue = createFunctionWithReturnValue; const addBuiltinProperty = (name) => { Object.defineProperty(exports, name, { enumerable: true, get: () => exports.getBuiltin(name) }); }; module_names_1.browserModuleNames .forEach(addBuiltinProperty); module.exports = require('../dist/src/main') { "name": "@electron/remote", "version": "2.1.2", "main": "renderer/index.js", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/electron/remote" }, "peerDependencies": { "electron": ">= 13.0.0" }, "devDependencies": { "@types/chai": "^4.2.11", "@types/chai-as-promised": "^7.1.2", "@types/dirty-chai": "^2.0.2", "@types/mocha": "^7.0.2", "@types/node": "^14.17.0", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "dirty-chai": "^2.0.1", "electron": "22.x", "mocha": "^10.1.0", "mocha-junit-reporter": "^1.23.3", "mocha-multi-reporters": "^1.1.7", "ts-node": "^8.10.2", "typescript": "^4.1.3", "walkdir": "^0.4.1", "yargs": "^15.3.1" }, "files": [ "README.md", "package.json", "main", "renderer", "dist/src", "index.d.ts" ], "types": "index.d.ts" }module.exports = require('../dist/src/renderer') The MIT License (MIT) Copyright (c) 2017 [Node.js API collaborators](https://github.com/nodejs/node-addon-api#collaborators) 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. { 'variables': { 'NAPI_VERSION%': " inline PropertyDescriptor PropertyDescriptor::Accessor( const char* utf8name, Getter getter, napi_property_attributes attributes, void* /*data*/) { using CbData = details::CallbackData; // TODO: Delete when the function is destroyed auto callbackData = new CbData({getter, nullptr}); return PropertyDescriptor({utf8name, nullptr, nullptr, CbData::Wrapper, nullptr, nullptr, attributes, callbackData}); } template inline PropertyDescriptor PropertyDescriptor::Accessor( const std::string& utf8name, Getter getter, napi_property_attributes attributes, void* data) { return Accessor(utf8name.c_str(), getter, attributes, data); } template inline PropertyDescriptor PropertyDescriptor::Accessor( napi_value name, Getter getter, napi_property_attributes attributes, void* /*data*/) { using CbData = details::CallbackData; // TODO: Delete when the function is destroyed auto callbackData = new CbData({getter, nullptr}); return PropertyDescriptor({nullptr, name, nullptr, CbData::Wrapper, nullptr, nullptr, attributes, callbackData}); } template inline PropertyDescriptor PropertyDescriptor::Accessor( Name name, Getter getter, napi_property_attributes attributes, void* data) { napi_value nameValue = name; return PropertyDescriptor::Accessor(nameValue, getter, attributes, data); } template inline PropertyDescriptor PropertyDescriptor::Accessor( const char* utf8name, Getter getter, Setter setter, napi_property_attributes attributes, void* /*data*/) { using CbData = details::AccessorCallbackData; // TODO: Delete when the function is destroyed auto callbackData = new CbData({getter, setter, nullptr}); return PropertyDescriptor({utf8name, nullptr, nullptr, CbData::GetterWrapper, CbData::SetterWrapper, nullptr, attributes, callbackData}); } template inline PropertyDescriptor PropertyDescriptor::Accessor( const std::string& utf8name, Getter getter, Setter setter, napi_property_attributes attributes, void* data) { return Accessor(utf8name.c_str(), getter, setter, attributes, data); } template inline PropertyDescriptor PropertyDescriptor::Accessor( napi_value name, Getter getter, Setter setter, napi_property_attributes attributes, void* /*data*/) { using CbData = details::AccessorCallbackData; // TODO: Delete when the function is destroyed auto callbackData = new CbData({getter, setter, nullptr}); return PropertyDescriptor({nullptr, name, nullptr, CbData::GetterWrapper, CbData::SetterWrapper, nullptr, attributes, callbackData}); } template inline PropertyDescriptor PropertyDescriptor::Accessor( Name name, Getter getter, Setter setter, napi_property_attributes attributes, void* data) { napi_value nameValue = name; return PropertyDescriptor::Accessor( nameValue, getter, setter, attributes, data); } template inline PropertyDescriptor PropertyDescriptor::Function( const char* utf8name, Callable cb, napi_property_attributes attributes, void* /*data*/) { using ReturnType = decltype(cb(CallbackInfo(nullptr, nullptr))); using CbData = details::CallbackData; // TODO: Delete when the function is destroyed auto callbackData = new CbData({cb, nullptr}); return PropertyDescriptor({utf8name, nullptr, CbData::Wrapper, nullptr, nullptr, nullptr, attributes, callbackData}); } template inline PropertyDescriptor PropertyDescriptor::Function( const std::string& utf8name, Callable cb, napi_property_attributes attributes, void* data) { return Function(utf8name.c_str(), cb, attributes, data); } template inline PropertyDescriptor PropertyDescriptor::Function( napi_value name, Callable cb, napi_property_attributes attributes, void* /*data*/) { using ReturnType = decltype(cb(CallbackInfo(nullptr, nullptr))); using CbData = details::CallbackData; // TODO: Delete when the function is destroyed auto callbackData = new CbData({cb, nullptr}); return PropertyDescriptor({nullptr, name, CbData::Wrapper, nullptr, nullptr, nullptr, attributes, callbackData}); } template inline PropertyDescriptor PropertyDescriptor::Function( Name name, Callable cb, napi_property_attributes attributes, void* data) { napi_value nameValue = name; return PropertyDescriptor::Function(nameValue, cb, attributes, data); } #endif // !SRC_NAPI_INL_DEPRECATED_H_ #ifndef SRC_NAPI_INL_H_ #define SRC_NAPI_INL_H_ //////////////////////////////////////////////////////////////////////////////// // Node-API C++ Wrapper Classes // // Inline header-only implementations for "Node-API" ABI-stable C APIs for // Node.js. //////////////////////////////////////////////////////////////////////////////// // Note: Do not include this file directly! Include "napi.h" instead. // This should be a no-op and is intended for better IDE integration. #include "napi.h" #include #include #include #if NAPI_HAS_THREADS #include #endif // NAPI_HAS_THREADS #include #include namespace Napi { #ifdef NAPI_CPP_CUSTOM_NAMESPACE namespace NAPI_CPP_CUSTOM_NAMESPACE { #endif // Helpers to handle functions exposed from C++ and internal constants. namespace details { // New napi_status constants not yet available in all supported versions of // Node.js releases. Only necessary when they are used in napi.h and napi-inl.h. constexpr int napi_no_external_buffers_allowed = 22; template inline void default_basic_finalizer(node_addon_api_basic_env /*env*/, void* data, void* /*hint*/) { delete static_cast(data); } // Attach a data item to an object and delete it when the object gets // garbage-collected. // TODO: Replace this code with `napi_add_finalizer()` whenever it becomes // available on all supported versions of Node.js. template < typename FreeType, node_addon_api_basic_finalize finalizer = default_basic_finalizer> inline napi_status AttachData(napi_env env, napi_value obj, FreeType* data, void* hint = nullptr) { napi_status status; #if (NAPI_VERSION < 5) napi_value symbol, external; status = napi_create_symbol(env, nullptr, &symbol); if (status == napi_ok) { status = napi_create_external(env, data, finalizer, hint, &external); if (status == napi_ok) { napi_property_descriptor desc = {nullptr, symbol, nullptr, nullptr, nullptr, external, napi_default, nullptr}; status = napi_define_properties(env, obj, 1, &desc); } } #else // NAPI_VERSION >= 5 status = napi_add_finalizer(env, obj, data, finalizer, hint, nullptr); #endif return status; } // For use in JS to C++ callback wrappers to catch any Napi::Error exceptions // and rethrow them as JavaScript exceptions before returning from the callback. template #ifdef NODE_ADDON_API_CPP_EXCEPTIONS_ALL inline napi_value WrapCallback(napi_env env, Callable callback) { #else inline napi_value WrapCallback(napi_env, Callable callback) { #endif #ifdef NODE_ADDON_API_CPP_EXCEPTIONS try { return callback(); } catch (const Error& e) { e.ThrowAsJavaScriptException(); return nullptr; } #ifdef NODE_ADDON_API_CPP_EXCEPTIONS_ALL catch (const std::exception& e) { Napi::Error::New(env, e.what()).ThrowAsJavaScriptException(); return nullptr; } catch (...) { Napi::Error::New(env, "A native exception was thrown") .ThrowAsJavaScriptException(); return nullptr; } #endif // NODE_ADDON_API_CPP_EXCEPTIONS_ALL #else // NODE_ADDON_API_CPP_EXCEPTIONS // When C++ exceptions are disabled, errors are immediately thrown as JS // exceptions, so there is no need to catch and rethrow them here. return callback(); #endif // NODE_ADDON_API_CPP_EXCEPTIONS } // For use in JS to C++ void callback wrappers to catch any Napi::Error // exceptions and rethrow them as JavaScript exceptions before returning from // the callback. template inline void WrapVoidCallback(Callable callback) { #ifdef NODE_ADDON_API_CPP_EXCEPTIONS try { callback(); } catch (const Error& e) { e.ThrowAsJavaScriptException(); } #else // NAPI_CPP_EXCEPTIONS // When C++ exceptions are disabled, errors are immediately thrown as JS // exceptions, so there is no need to catch and rethrow them here. callback(); #endif // NAPI_CPP_EXCEPTIONS } // For use in JS to C++ void callback wrappers to catch _any_ thrown exception // and rethrow them as JavaScript exceptions before returning from the callback, // wrapping in an Napi::Error as needed. template #ifdef NODE_ADDON_API_CPP_EXCEPTIONS_ALL inline void WrapVoidCallback(napi_env env, Callable callback) { #else inline void WrapVoidCallback(napi_env, Callable callback) { #endif #ifdef NODE_ADDON_API_CPP_EXCEPTIONS try { callback(); } catch (const Error& e) { e.ThrowAsJavaScriptException(); } #ifdef NODE_ADDON_API_CPP_EXCEPTIONS_ALL catch (const std::exception& e) { Napi::Error::New(env, e.what()).ThrowAsJavaScriptException(); } catch (...) { Napi::Error::New(env, "A native exception was thrown") .ThrowAsJavaScriptException(); } #endif // NODE_ADDON_API_CPP_EXCEPTIONS_ALL #else // When C++ exceptions are disabled, there is no need to catch and rethrow C++ // exceptions. JS errors should be thrown with // `Error::ThrowAsJavaScriptException`. callback(); #endif // NODE_ADDON_API_CPP_EXCEPTIONS } template struct CallbackData { static inline napi_value Wrapper(napi_env env, napi_callback_info info) { return details::WrapCallback(env, [&] { CallbackInfo callbackInfo(env, info); CallbackData* callbackData = static_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); return callbackData->callback(callbackInfo); }); } Callable callback; void* data; }; template struct CallbackData { static inline napi_value Wrapper(napi_env env, napi_callback_info info) { return details::WrapCallback(env, [&] { CallbackInfo callbackInfo(env, info); CallbackData* callbackData = static_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); callbackData->callback(callbackInfo); return nullptr; }); } Callable callback; void* data; }; template napi_value TemplatedVoidCallback(napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback(env, [&] { CallbackInfo cbInfo(env, info); Callback(cbInfo); return nullptr; }); } template napi_value TemplatedCallback(napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback(env, [&] { CallbackInfo cbInfo(env, info); // MSVC requires to copy 'Callback' function pointer to a local variable // before invoking it. auto callback = Callback; return callback(cbInfo); }); } template napi_value TemplatedInstanceCallback(napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback(env, [&] { CallbackInfo cbInfo(env, info); T* instance = T::Unwrap(cbInfo.This().As()); return instance ? (instance->*UnwrapCallback)(cbInfo) : Napi::Value(); }); } template napi_value TemplatedInstanceVoidCallback(napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback(env, [&] { CallbackInfo cbInfo(env, info); T* instance = T::Unwrap(cbInfo.This().As()); if (instance) (instance->*UnwrapCallback)(cbInfo); return nullptr; }); } template struct FinalizeData { #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER template >> #endif static inline void Wrapper(node_addon_api_basic_env env, void* data, void* finalizeHint) NAPI_NOEXCEPT { WrapVoidCallback([&] { FinalizeData* finalizeData = static_cast(finalizeHint); finalizeData->callback(env, static_cast(data)); delete finalizeData; }); } #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER template >, typename = void> static inline void Wrapper(node_addon_api_basic_env env, void* data, void* finalizeHint) NAPI_NOEXCEPT { #ifdef NODE_ADDON_API_REQUIRE_BASIC_FINALIZERS static_assert(false, "NODE_ADDON_API_REQUIRE_BASIC_FINALIZERS defined: Finalizer " "must be basic."); #endif napi_status status = node_api_post_finalizer(env, WrapperGC, data, finalizeHint); NAPI_FATAL_IF_FAILED( status, "FinalizeData::Wrapper", "node_api_post_finalizer failed"); } #endif #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER template >> #endif static inline void WrapperWithHint(node_addon_api_basic_env env, void* data, void* finalizeHint) NAPI_NOEXCEPT { WrapVoidCallback([&] { FinalizeData* finalizeData = static_cast(finalizeHint); finalizeData->callback(env, static_cast(data), finalizeData->hint); delete finalizeData; }); } #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER template >, typename = void> static inline void WrapperWithHint(node_addon_api_basic_env env, void* data, void* finalizeHint) NAPI_NOEXCEPT { #ifdef NODE_ADDON_API_REQUIRE_BASIC_FINALIZERS static_assert(false, "NODE_ADDON_API_REQUIRE_BASIC_FINALIZERS defined: Finalizer " "must be basic."); #endif napi_status status = node_api_post_finalizer(env, WrapperGCWithHint, data, finalizeHint); NAPI_FATAL_IF_FAILED( status, "FinalizeData::Wrapper", "node_api_post_finalizer failed"); } #endif static inline void WrapperGCWithoutData(napi_env env, void* /*data*/, void* finalizeHint) NAPI_NOEXCEPT { WrapVoidCallback(env, [&] { FinalizeData* finalizeData = static_cast(finalizeHint); finalizeData->callback(env); delete finalizeData; }); } static inline void WrapperGC(napi_env env, void* data, void* finalizeHint) NAPI_NOEXCEPT { WrapVoidCallback(env, [&] { FinalizeData* finalizeData = static_cast(finalizeHint); finalizeData->callback(env, static_cast(data)); delete finalizeData; }); } static inline void WrapperGCWithHint(napi_env env, void* data, void* finalizeHint) NAPI_NOEXCEPT { WrapVoidCallback(env, [&] { FinalizeData* finalizeData = static_cast(finalizeHint); finalizeData->callback(env, static_cast(data), finalizeData->hint); delete finalizeData; }); } Finalizer callback; Hint* hint; }; #if (NAPI_VERSION > 3 && NAPI_HAS_THREADS) template , typename FinalizerDataType = void> struct ThreadSafeFinalize { static inline void Wrapper(napi_env env, void* rawFinalizeData, void* /* rawContext */) { if (rawFinalizeData == nullptr) return; ThreadSafeFinalize* finalizeData = static_cast(rawFinalizeData); finalizeData->callback(Env(env)); delete finalizeData; } static inline void FinalizeWrapperWithData(napi_env env, void* rawFinalizeData, void* /* rawContext */) { if (rawFinalizeData == nullptr) return; ThreadSafeFinalize* finalizeData = static_cast(rawFinalizeData); finalizeData->callback(Env(env), finalizeData->data); delete finalizeData; } static inline void FinalizeWrapperWithContext(napi_env env, void* rawFinalizeData, void* rawContext) { if (rawFinalizeData == nullptr) return; ThreadSafeFinalize* finalizeData = static_cast(rawFinalizeData); finalizeData->callback(Env(env), static_cast(rawContext)); delete finalizeData; } static inline void FinalizeFinalizeWrapperWithDataAndContext( napi_env env, void* rawFinalizeData, void* rawContext) { if (rawFinalizeData == nullptr) return; ThreadSafeFinalize* finalizeData = static_cast(rawFinalizeData); finalizeData->callback( Env(env), finalizeData->data, static_cast(rawContext)); delete finalizeData; } FinalizerDataType* data; Finalizer callback; }; template inline typename std::enable_if(nullptr)>::type CallJsWrapper(napi_env env, napi_value jsCallback, void* context, void* data) { details::WrapVoidCallback(env, [&]() { call(env, Function(env, jsCallback), static_cast(context), static_cast(data)); }); } template inline typename std::enable_if(nullptr)>::type CallJsWrapper(napi_env env, napi_value jsCallback, void* /*context*/, void* /*data*/) { details::WrapVoidCallback(env, [&]() { if (jsCallback != nullptr) { Function(env, jsCallback).Call(0, nullptr); } }); } #if NAPI_VERSION > 4 template napi_value DefaultCallbackWrapper(napi_env /*env*/, std::nullptr_t /*cb*/) { return nullptr; } template napi_value DefaultCallbackWrapper(napi_env /*env*/, Napi::Function cb) { return cb; } #else template napi_value DefaultCallbackWrapper(napi_env env, Napi::Function cb) { if (cb.IsEmpty()) { return TSFN::EmptyFunctionFactory(env); } return cb; } #endif // NAPI_VERSION > 4 #endif // NAPI_VERSION > 3 && NAPI_HAS_THREADS template struct AccessorCallbackData { static inline napi_value GetterWrapper(napi_env env, napi_callback_info info) { return details::WrapCallback(env, [&] { CallbackInfo callbackInfo(env, info); AccessorCallbackData* callbackData = static_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); return callbackData->getterCallback(callbackInfo); }); } static inline napi_value SetterWrapper(napi_env env, napi_callback_info info) { return details::WrapCallback(env, [&] { CallbackInfo callbackInfo(env, info); AccessorCallbackData* callbackData = static_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); callbackData->setterCallback(callbackInfo); return nullptr; }); } Getter getterCallback; Setter setterCallback; void* data; }; // Debugging-purpose C++-style variant of sprintf(). inline std::string StringFormat(const char* format, ...) { std::string result; va_list args; va_start(args, format); int len = vsnprintf(nullptr, 0, format, args); result.resize(len); vsnprintf(&result[0], len + 1, format, args); va_end(args); return result; } template class HasExtendedFinalizer { private: template struct SFINAE {}; template static char test(SFINAE*); template static int test(...); public: static constexpr bool value = sizeof(test(0)) == sizeof(char); }; template class HasBasicFinalizer { private: template struct SFINAE {}; template static char test(SFINAE*); template static int test(...); public: static constexpr bool value = sizeof(test(0)) == sizeof(char); }; } // namespace details #ifndef NODE_ADDON_API_DISABLE_DEPRECATED #include "napi-inl.deprecated.h" #endif // !NODE_ADDON_API_DISABLE_DEPRECATED //////////////////////////////////////////////////////////////////////////////// // Module registration //////////////////////////////////////////////////////////////////////////////// // Register an add-on based on an initializer function. #define NODE_API_MODULE(modname, regfunc) \ static napi_value __napi_##regfunc(napi_env env, napi_value exports) { \ return Napi::RegisterModule(env, exports, regfunc); \ } \ NAPI_MODULE(modname, __napi_##regfunc) // Register an add-on based on a subclass of `Addon` with a custom Node.js // module name. #define NODE_API_NAMED_ADDON(modname, classname) \ static napi_value __napi_##classname(napi_env env, napi_value exports) { \ return Napi::RegisterModule(env, exports, &classname::Init); \ } \ NAPI_MODULE(modname, __napi_##classname) // Register an add-on based on a subclass of `Addon` with the Node.js module // name given by node-gyp from the `target_name` in binding.gyp. #define NODE_API_ADDON(classname) \ NODE_API_NAMED_ADDON(NODE_GYP_MODULE_NAME, classname) // Adapt the NAPI_MODULE registration function: // - Wrap the arguments in NAPI wrappers. // - Catch any NAPI errors and rethrow as JS exceptions. inline napi_value RegisterModule(napi_env env, napi_value exports, ModuleRegisterCallback registerCallback) { return details::WrapCallback(env, [&] { return napi_value( registerCallback(Napi::Env(env), Napi::Object(env, exports))); }); } //////////////////////////////////////////////////////////////////////////////// // Maybe class //////////////////////////////////////////////////////////////////////////////// template bool Maybe::IsNothing() const { return !_has_value; } template bool Maybe::IsJust() const { return _has_value; } template void Maybe::Check() const { NAPI_CHECK(IsJust(), "Napi::Maybe::Check", "Maybe value is Nothing."); } template T Maybe::Unwrap() const { NAPI_CHECK(IsJust(), "Napi::Maybe::Unwrap", "Maybe value is Nothing."); return _value; } template T Maybe::UnwrapOr(const T& default_value) const { return _has_value ? _value : default_value; } template bool Maybe::UnwrapTo(T* out) const { if (IsJust()) { *out = _value; return true; }; return false; } template bool Maybe::operator==(const Maybe& other) const { return (IsJust() == other.IsJust()) && (!IsJust() || Unwrap() == other.Unwrap()); } template bool Maybe::operator!=(const Maybe& other) const { return !operator==(other); } template Maybe::Maybe() : _has_value(false) {} template Maybe::Maybe(const T& t) : _has_value(true), _value(t) {} template inline Maybe Nothing() { return Maybe(); } template inline Maybe Just(const T& t) { return Maybe(t); } //////////////////////////////////////////////////////////////////////////////// // BasicEnv / Env class //////////////////////////////////////////////////////////////////////////////// inline BasicEnv::BasicEnv(node_addon_api_basic_env env) : _env(env) {} inline BasicEnv::operator node_addon_api_basic_env() const { return _env; } inline Env::Env(napi_env env) : BasicEnv(env) {} inline Env::operator napi_env() const { return const_cast(_env); } inline Object Env::Global() const { napi_value value; napi_status status = napi_get_global(*this, &value); NAPI_THROW_IF_FAILED(*this, status, Object()); return Object(*this, value); } inline Value Env::Undefined() const { napi_value value; napi_status status = napi_get_undefined(*this, &value); NAPI_THROW_IF_FAILED(*this, status, Value()); return Value(*this, value); } inline Value Env::Null() const { napi_value value; napi_status status = napi_get_null(*this, &value); NAPI_THROW_IF_FAILED(*this, status, Value()); return Value(*this, value); } inline bool Env::IsExceptionPending() const { bool result; napi_status status = napi_is_exception_pending(*this, &result); if (status != napi_ok) result = false; // Checking for a pending exception shouldn't throw. return result; } inline Error Env::GetAndClearPendingException() const { napi_value value; napi_status status = napi_get_and_clear_last_exception(*this, &value); if (status != napi_ok) { // Don't throw another exception when failing to get the exception! return Error(); } return Error(*this, value); } inline MaybeOrValue Env::RunScript(const char* utf8script) const { String script = String::New(*this, utf8script); return RunScript(script); } inline MaybeOrValue Env::RunScript(const std::string& utf8script) const { return RunScript(utf8script.c_str()); } inline MaybeOrValue Env::RunScript(String script) const { napi_value result; napi_status status = napi_run_script(*this, script, &result); NAPI_RETURN_OR_THROW_IF_FAILED( *this, status, Napi::Value(*this, result), Napi::Value); } #if NAPI_VERSION > 2 template void BasicEnv::CleanupHook::Wrapper(void* data) NAPI_NOEXCEPT { auto* cleanupData = static_cast< typename Napi::BasicEnv::CleanupHook::CleanupData*>(data); cleanupData->hook(); delete cleanupData; } template void BasicEnv::CleanupHook::WrapperWithArg(void* data) NAPI_NOEXCEPT { auto* cleanupData = static_cast< typename Napi::BasicEnv::CleanupHook::CleanupData*>(data); cleanupData->hook(static_cast(cleanupData->arg)); delete cleanupData; } #endif // NAPI_VERSION > 2 #if NAPI_VERSION > 5 template fini> inline void BasicEnv::SetInstanceData(T* data) const { napi_status status = napi_set_instance_data( _env, data, [](napi_env env, void* data, void*) { fini(env, static_cast(data)); }, nullptr); NAPI_FATAL_IF_FAILED( status, "BasicEnv::SetInstanceData", "invalid arguments"); } template fini> inline void BasicEnv::SetInstanceData(DataType* data, HintType* hint) const { napi_status status = napi_set_instance_data( _env, data, [](napi_env env, void* data, void* hint) { fini(env, static_cast(data), static_cast(hint)); }, hint); NAPI_FATAL_IF_FAILED( status, "BasicEnv::SetInstanceData", "invalid arguments"); } template inline T* BasicEnv::GetInstanceData() const { void* data = nullptr; napi_status status = napi_get_instance_data(_env, &data); NAPI_FATAL_IF_FAILED( status, "BasicEnv::GetInstanceData", "invalid arguments"); return static_cast(data); } template void BasicEnv::DefaultFini(Env, T* data) { delete data; } template void BasicEnv::DefaultFiniWithHint(Env, DataType* data, HintType*) { delete data; } #endif // NAPI_VERSION > 5 #if NAPI_VERSION > 8 inline const char* BasicEnv::GetModuleFileName() const { const char* result; napi_status status = node_api_get_module_file_name(_env, &result); NAPI_FATAL_IF_FAILED( status, "BasicEnv::GetModuleFileName", "invalid arguments"); return result; } #endif // NAPI_VERSION > 8 //////////////////////////////////////////////////////////////////////////////// // Value class //////////////////////////////////////////////////////////////////////////////// inline Value::Value() : _env(nullptr), _value(nullptr) {} inline Value::Value(napi_env env, napi_value value) : _env(env), _value(value) {} inline Value::operator napi_value() const { return _value; } inline bool Value::operator==(const Value& other) const { return StrictEquals(other); } inline bool Value::operator!=(const Value& other) const { return !this->operator==(other); } inline bool Value::StrictEquals(const Value& other) const { bool result; napi_status status = napi_strict_equals(_env, *this, other, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline Napi::Env Value::Env() const { return Napi::Env(_env); } inline bool Value::IsEmpty() const { return _value == nullptr; } inline napi_valuetype Value::Type() const { if (IsEmpty()) { return napi_undefined; } napi_valuetype type; napi_status status = napi_typeof(_env, _value, &type); NAPI_THROW_IF_FAILED(_env, status, napi_undefined); return type; } inline bool Value::IsUndefined() const { return Type() == napi_undefined; } inline bool Value::IsNull() const { return Type() == napi_null; } inline bool Value::IsBoolean() const { return Type() == napi_boolean; } inline bool Value::IsNumber() const { return Type() == napi_number; } #if NAPI_VERSION > 5 inline bool Value::IsBigInt() const { return Type() == napi_bigint; } #endif // NAPI_VERSION > 5 #if (NAPI_VERSION > 4) inline bool Value::IsDate() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_date(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } #endif inline bool Value::IsString() const { return Type() == napi_string; } inline bool Value::IsSymbol() const { return Type() == napi_symbol; } inline bool Value::IsArray() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_array(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsArrayBuffer() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_arraybuffer(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsTypedArray() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_typedarray(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsObject() const { return Type() == napi_object || IsFunction(); } inline bool Value::IsFunction() const { return Type() == napi_function; } inline bool Value::IsPromise() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_promise(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsDataView() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_dataview(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsBuffer() const { if (IsEmpty()) { return false; } bool result; napi_status status = napi_is_buffer(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } inline bool Value::IsExternal() const { return Type() == napi_external; } template inline T Value::As() const { #ifdef NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS T::CheckCast(_env, _value); #endif return T(_env, _value); } template inline T Value::UnsafeAs() const { return T(_env, _value); } // static inline void Value::CheckCast(napi_env /* env */, napi_value value) { NAPI_CHECK(value != nullptr, "Value::CheckCast", "empty value"); } inline MaybeOrValue Value::ToBoolean() const { napi_value result; napi_status status = napi_coerce_to_bool(_env, _value, &result); NAPI_RETURN_OR_THROW_IF_FAILED( _env, status, Napi::Boolean(_env, result), Napi::Boolean); } inline MaybeOrValue Value::ToNumber() const { napi_value result; napi_status status = napi_coerce_to_number(_env, _value, &result); NAPI_RETURN_OR_THROW_IF_FAILED( _env, status, Napi::Number(_env, result), Napi::Number); } inline MaybeOrValue Value::ToString() const { napi_value result; napi_status status = napi_coerce_to_string(_env, _value, &result); NAPI_RETURN_OR_THROW_IF_FAILED( _env, status, Napi::String(_env, result), Napi::String); } inline MaybeOrValue Value::ToObject() const { napi_value result; napi_status status = napi_coerce_to_object(_env, _value, &result); NAPI_RETURN_OR_THROW_IF_FAILED( _env, status, Napi::Object(_env, result), Napi::Object); } //////////////////////////////////////////////////////////////////////////////// // Boolean class //////////////////////////////////////////////////////////////////////////////// inline Boolean Boolean::New(napi_env env, bool val) { napi_value value; napi_status status = napi_get_boolean(env, val, &value); NAPI_THROW_IF_FAILED(env, status, Boolean()); return Boolean(env, value); } inline void Boolean::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "Boolean::CheckCast", "empty value"); napi_valuetype type; napi_status status = napi_typeof(env, value, &type); NAPI_CHECK(status == napi_ok, "Boolean::CheckCast", "napi_typeof failed"); NAPI_INTERNAL_CHECK_EQ(type, napi_boolean, "%d", "Boolean::CheckCast"); } inline Boolean::Boolean() : Napi::Value() {} inline Boolean::Boolean(napi_env env, napi_value value) : Napi::Value(env, value) {} inline Boolean::operator bool() const { return Value(); } inline bool Boolean::Value() const { bool result; napi_status status = napi_get_value_bool(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } //////////////////////////////////////////////////////////////////////////////// // Number class //////////////////////////////////////////////////////////////////////////////// inline Number Number::New(napi_env env, double val) { napi_value value; napi_status status = napi_create_double(env, val, &value); NAPI_THROW_IF_FAILED(env, status, Number()); return Number(env, value); } inline void Number::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "Number::CheckCast", "empty value"); napi_valuetype type; napi_status status = napi_typeof(env, value, &type); NAPI_CHECK(status == napi_ok, "Number::CheckCast", "napi_typeof failed"); NAPI_INTERNAL_CHECK_EQ(type, napi_number, "%d", "Number::CheckCast"); } inline Number::Number() : Value() {} inline Number::Number(napi_env env, napi_value value) : Value(env, value) {} inline Number::operator int32_t() const { return Int32Value(); } inline Number::operator uint32_t() const { return Uint32Value(); } inline Number::operator int64_t() const { return Int64Value(); } inline Number::operator float() const { return FloatValue(); } inline Number::operator double() const { return DoubleValue(); } inline int32_t Number::Int32Value() const { int32_t result; napi_status status = napi_get_value_int32(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } inline uint32_t Number::Uint32Value() const { uint32_t result; napi_status status = napi_get_value_uint32(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } inline int64_t Number::Int64Value() const { int64_t result; napi_status status = napi_get_value_int64(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } inline float Number::FloatValue() const { return static_cast(DoubleValue()); } inline double Number::DoubleValue() const { double result; napi_status status = napi_get_value_double(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } #if NAPI_VERSION > 5 //////////////////////////////////////////////////////////////////////////////// // BigInt Class //////////////////////////////////////////////////////////////////////////////// inline BigInt BigInt::New(napi_env env, int64_t val) { napi_value value; napi_status status = napi_create_bigint_int64(env, val, &value); NAPI_THROW_IF_FAILED(env, status, BigInt()); return BigInt(env, value); } inline BigInt BigInt::New(napi_env env, uint64_t val) { napi_value value; napi_status status = napi_create_bigint_uint64(env, val, &value); NAPI_THROW_IF_FAILED(env, status, BigInt()); return BigInt(env, value); } inline BigInt BigInt::New(napi_env env, int sign_bit, size_t word_count, const uint64_t* words) { napi_value value; napi_status status = napi_create_bigint_words(env, sign_bit, word_count, words, &value); NAPI_THROW_IF_FAILED(env, status, BigInt()); return BigInt(env, value); } inline void BigInt::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "BigInt::CheckCast", "empty value"); napi_valuetype type; napi_status status = napi_typeof(env, value, &type); NAPI_CHECK(status == napi_ok, "BigInt::CheckCast", "napi_typeof failed"); NAPI_INTERNAL_CHECK_EQ(type, napi_bigint, "%d", "BigInt::CheckCast"); } inline BigInt::BigInt() : Value() {} inline BigInt::BigInt(napi_env env, napi_value value) : Value(env, value) {} inline int64_t BigInt::Int64Value(bool* lossless) const { int64_t result; napi_status status = napi_get_value_bigint_int64(_env, _value, &result, lossless); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } inline uint64_t BigInt::Uint64Value(bool* lossless) const { uint64_t result; napi_status status = napi_get_value_bigint_uint64(_env, _value, &result, lossless); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } inline size_t BigInt::WordCount() const { size_t word_count; napi_status status = napi_get_value_bigint_words(_env, _value, nullptr, &word_count, nullptr); NAPI_THROW_IF_FAILED(_env, status, 0); return word_count; } inline void BigInt::ToWords(int* sign_bit, size_t* word_count, uint64_t* words) { napi_status status = napi_get_value_bigint_words(_env, _value, sign_bit, word_count, words); NAPI_THROW_IF_FAILED_VOID(_env, status); } #endif // NAPI_VERSION > 5 #if (NAPI_VERSION > 4) //////////////////////////////////////////////////////////////////////////////// // Date Class //////////////////////////////////////////////////////////////////////////////// inline Date Date::New(napi_env env, double val) { napi_value value; napi_status status = napi_create_date(env, val, &value); NAPI_THROW_IF_FAILED(env, status, Date()); return Date(env, value); } inline void Date::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "Date::CheckCast", "empty value"); bool result; napi_status status = napi_is_date(env, value, &result); NAPI_CHECK(status == napi_ok, "Date::CheckCast", "napi_is_date failed"); NAPI_CHECK(result, "Date::CheckCast", "value is not date"); } inline Date::Date() : Value() {} inline Date::Date(napi_env env, napi_value value) : Value(env, value) {} inline Date::operator double() const { return ValueOf(); } inline double Date::ValueOf() const { double result; napi_status status = napi_get_date_value(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } #endif //////////////////////////////////////////////////////////////////////////////// // Name class //////////////////////////////////////////////////////////////////////////////// inline void Name::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "Name::CheckCast", "empty value"); napi_valuetype type; napi_status status = napi_typeof(env, value, &type); NAPI_CHECK(status == napi_ok, "Name::CheckCast", "napi_typeof failed"); NAPI_INTERNAL_CHECK(type == napi_string || type == napi_symbol, "Name::CheckCast", "value is not napi_string or napi_symbol, got %d.", type); } inline Name::Name() : Value() {} inline Name::Name(napi_env env, napi_value value) : Value(env, value) {} //////////////////////////////////////////////////////////////////////////////// // String class //////////////////////////////////////////////////////////////////////////////// inline String String::New(napi_env env, const std::string& val) { return String::New(env, val.c_str(), val.size()); } inline String String::New(napi_env env, const std::u16string& val) { return String::New(env, val.c_str(), val.size()); } inline String String::New(napi_env env, const char* val) { // TODO(@gabrielschulhof) Remove if-statement when core's error handling is // available in all supported versions. if (val == nullptr) { // Throw an error that looks like it came from core. NAPI_THROW_IF_FAILED(env, napi_invalid_arg, String()); } napi_value value; napi_status status = napi_create_string_utf8(env, val, std::strlen(val), &value); NAPI_THROW_IF_FAILED(env, status, String()); return String(env, value); } inline String String::New(napi_env env, const char16_t* val) { napi_value value; // TODO(@gabrielschulhof) Remove if-statement when core's error handling is // available in all supported versions. if (val == nullptr) { // Throw an error that looks like it came from core. NAPI_THROW_IF_FAILED(env, napi_invalid_arg, String()); } napi_status status = napi_create_string_utf16(env, val, std::u16string(val).size(), &value); NAPI_THROW_IF_FAILED(env, status, String()); return String(env, value); } inline String String::New(napi_env env, const char* val, size_t length) { napi_value value; napi_status status = napi_create_string_utf8(env, val, length, &value); NAPI_THROW_IF_FAILED(env, status, String()); return String(env, value); } inline String String::New(napi_env env, const char16_t* val, size_t length) { napi_value value; napi_status status = napi_create_string_utf16(env, val, length, &value); NAPI_THROW_IF_FAILED(env, status, String()); return String(env, value); } inline void String::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "String::CheckCast", "empty value"); napi_valuetype type; napi_status status = napi_typeof(env, value, &type); NAPI_CHECK(status == napi_ok, "String::CheckCast", "napi_typeof failed"); NAPI_INTERNAL_CHECK_EQ(type, napi_string, "%d", "String::CheckCast"); } inline String::String() : Name() {} inline String::String(napi_env env, napi_value value) : Name(env, value) {} inline String::operator std::string() const { return Utf8Value(); } inline String::operator std::u16string() const { return Utf16Value(); } inline std::string String::Utf8Value() const { size_t length; napi_status status = napi_get_value_string_utf8(_env, _value, nullptr, 0, &length); NAPI_THROW_IF_FAILED(_env, status, ""); std::string value; value.reserve(length + 1); value.resize(length); status = napi_get_value_string_utf8( _env, _value, &value[0], value.capacity(), nullptr); NAPI_THROW_IF_FAILED(_env, status, ""); return value; } inline std::u16string String::Utf16Value() const { size_t length; napi_status status = napi_get_value_string_utf16(_env, _value, nullptr, 0, &length); NAPI_THROW_IF_FAILED(_env, status, NAPI_WIDE_TEXT("")); std::u16string value; value.reserve(length + 1); value.resize(length); status = napi_get_value_string_utf16( _env, _value, &value[0], value.capacity(), nullptr); NAPI_THROW_IF_FAILED(_env, status, NAPI_WIDE_TEXT("")); return value; } //////////////////////////////////////////////////////////////////////////////// // Symbol class //////////////////////////////////////////////////////////////////////////////// inline Symbol Symbol::New(napi_env env, const char* description) { napi_value descriptionValue = description != nullptr ? String::New(env, description) : static_cast(nullptr); return Symbol::New(env, descriptionValue); } inline Symbol Symbol::New(napi_env env, const std::string& description) { napi_value descriptionValue = String::New(env, description); return Symbol::New(env, descriptionValue); } inline Symbol Symbol::New(napi_env env, String description) { napi_value descriptionValue = description; return Symbol::New(env, descriptionValue); } inline Symbol Symbol::New(napi_env env, napi_value description) { napi_value value; napi_status status = napi_create_symbol(env, description, &value); NAPI_THROW_IF_FAILED(env, status, Symbol()); return Symbol(env, value); } inline MaybeOrValue Symbol::WellKnown(napi_env env, const std::string& name) { // No need to check if the return value is a symbol or undefined. // Well known symbols are definite and it is an develop time error // if the symbol does not exist. #if defined(NODE_ADDON_API_ENABLE_MAYBE) Value symbol_obj; Value symbol_value; if (Napi::Env(env).Global().Get("Symbol").UnwrapTo(&symbol_obj) && symbol_obj.As().Get(name).UnwrapTo(&symbol_value)) { return Just(symbol_value.UnsafeAs()); } return Nothing(); #else return Napi::Env(env) .Global() .Get("Symbol") .As() .Get(name) .UnsafeAs(); #endif } inline MaybeOrValue Symbol::For(napi_env env, const std::string& description) { napi_value descriptionValue = String::New(env, description); return Symbol::For(env, descriptionValue); } inline MaybeOrValue Symbol::For(napi_env env, const char* description) { napi_value descriptionValue = String::New(env, description); return Symbol::For(env, descriptionValue); } inline MaybeOrValue Symbol::For(napi_env env, String description) { return Symbol::For(env, static_cast(description)); } inline MaybeOrValue Symbol::For(napi_env env, napi_value description) { #if defined(NODE_ADDON_API_ENABLE_MAYBE) Value symbol_obj; Value symbol_for_value; Value symbol_value; if (Napi::Env(env).Global().Get("Symbol").UnwrapTo(&symbol_obj) && symbol_obj.As().Get("for").UnwrapTo(&symbol_for_value) && symbol_for_value.As() .Call(symbol_obj, {description}) .UnwrapTo(&symbol_value)) { return Just(symbol_value.As()); } return Nothing(); #else Object symbol_obj = Napi::Env(env).Global().Get("Symbol").As(); return symbol_obj.Get("for") .As() .Call(symbol_obj, {description}) .As(); #endif } inline void Symbol::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "Symbol::CheckCast", "empty value"); napi_valuetype type; napi_status status = napi_typeof(env, value, &type); NAPI_CHECK(status == napi_ok, "Symbol::CheckCast", "napi_typeof failed"); NAPI_INTERNAL_CHECK_EQ(type, napi_symbol, "%d", "Symbol::CheckCast"); } inline Symbol::Symbol() : Name() {} inline Symbol::Symbol(napi_env env, napi_value value) : Name(env, value) {} //////////////////////////////////////////////////////////////////////////////// // Automagic value creation //////////////////////////////////////////////////////////////////////////////// namespace details { template struct vf_number { static Number From(napi_env env, T value) { return Number::New(env, static_cast(value)); } }; template <> struct vf_number { static Boolean From(napi_env env, bool value) { return Boolean::New(env, value); } }; struct vf_utf8_charp { static String From(napi_env env, const char* value) { return String::New(env, value); } }; struct vf_utf16_charp { static String From(napi_env env, const char16_t* value) { return String::New(env, value); } }; struct vf_utf8_string { static String From(napi_env env, const std::string& value) { return String::New(env, value); } }; struct vf_utf16_string { static String From(napi_env env, const std::u16string& value) { return String::New(env, value); } }; template struct vf_fallback { static Value From(napi_env env, const T& value) { return Value(env, value); } }; template struct disjunction : std::false_type {}; template struct disjunction : B {}; template struct disjunction : std::conditional>::type {}; template struct can_make_string : disjunction::type, typename std::is_convertible::type, typename std::is_convertible::type, typename std::is_convertible::type> {}; } // namespace details template Value Value::From(napi_env env, const T& value) { using Helper = typename std::conditional< std::is_integral::value || std::is_floating_point::value, details::vf_number, typename std::conditional::value, String, details::vf_fallback>::type>::type; return Helper::From(env, value); } template String String::From(napi_env env, const T& value) { struct Dummy {}; using Helper = typename std::conditional< std::is_convertible::value, details::vf_utf8_charp, typename std::conditional< std::is_convertible::value, details::vf_utf16_charp, typename std::conditional< std::is_convertible::value, details::vf_utf8_string, typename std::conditional< std::is_convertible::value, details::vf_utf16_string, Dummy>::type>::type>::type>::type; return Helper::From(env, value); } //////////////////////////////////////////////////////////////////////////////// // TypeTaggable class //////////////////////////////////////////////////////////////////////////////// inline TypeTaggable::TypeTaggable() : Value() {} inline TypeTaggable::TypeTaggable(napi_env _env, napi_value _value) : Value(_env, _value) {} #if NAPI_VERSION >= 8 inline void TypeTaggable::TypeTag(const napi_type_tag* type_tag) const { napi_status status = napi_type_tag_object(_env, _value, type_tag); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline bool TypeTaggable::CheckTypeTag(const napi_type_tag* type_tag) const { bool result; napi_status status = napi_check_object_type_tag(_env, _value, type_tag, &result); NAPI_THROW_IF_FAILED(_env, status, false); return result; } #endif // NAPI_VERSION >= 8 //////////////////////////////////////////////////////////////////////////////// // Object class //////////////////////////////////////////////////////////////////////////////// template inline Object::PropertyLValue::operator Value() const { MaybeOrValue val = Object(_env, _object).Get(_key); #ifdef NODE_ADDON_API_ENABLE_MAYBE return val.Unwrap(); #else return val; #endif } template template inline Object::PropertyLValue& Object::PropertyLValue::operator=( ValueType value) { #ifdef NODE_ADDON_API_ENABLE_MAYBE MaybeOrValue result = #endif Object(_env, _object).Set(_key, value); #ifdef NODE_ADDON_API_ENABLE_MAYBE result.Unwrap(); #endif return *this; } template inline Object::PropertyLValue::PropertyLValue(Object object, Key key) : _env(object.Env()), _object(object), _key(key) {} inline Object Object::New(napi_env env) { napi_value value; napi_status status = napi_create_object(env, &value); NAPI_THROW_IF_FAILED(env, status, Object()); return Object(env, value); } inline void Object::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "Object::CheckCast", "empty value"); napi_valuetype type; napi_status status = napi_typeof(env, value, &type); NAPI_CHECK(status == napi_ok, "Object::CheckCast", "napi_typeof failed"); NAPI_INTERNAL_CHECK(type == napi_object || type == napi_function, "Object::CheckCast", "Expect napi_object or napi_function, but got %d.", type); } inline Object::Object() : TypeTaggable() {} inline Object::Object(napi_env env, napi_value value) : TypeTaggable(env, value) {} inline Object::PropertyLValue Object::operator[]( const char* utf8name) { return PropertyLValue(*this, utf8name); } inline Object::PropertyLValue Object::operator[]( const std::string& utf8name) { return PropertyLValue(*this, utf8name); } inline Object::PropertyLValue Object::operator[](uint32_t index) { return PropertyLValue(*this, index); } inline Object::PropertyLValue Object::operator[](Value index) const { return PropertyLValue(*this, index); } inline MaybeOrValue Object::operator[](const char* utf8name) const { return Get(utf8name); } inline MaybeOrValue Object::operator[]( const std::string& utf8name) const { return Get(utf8name); } inline MaybeOrValue Object::operator[](uint32_t index) const { return Get(index); } inline MaybeOrValue Object::Has(napi_value key) const { bool result; napi_status status = napi_has_property(_env, _value, key, &result); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool); } inline MaybeOrValue Object::Has(Value key) const { bool result; napi_status status = napi_has_property(_env, _value, key, &result); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool); } inline MaybeOrValue Object::Has(const char* utf8name) const { bool result; napi_status status = napi_has_named_property(_env, _value, utf8name, &result); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool); } inline MaybeOrValue Object::Has(const std::string& utf8name) const { return Has(utf8name.c_str()); } inline MaybeOrValue Object::HasOwnProperty(napi_value key) const { bool result; napi_status status = napi_has_own_property(_env, _value, key, &result); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool); } inline MaybeOrValue Object::HasOwnProperty(Value key) const { bool result; napi_status status = napi_has_own_property(_env, _value, key, &result); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool); } inline MaybeOrValue Object::HasOwnProperty(const char* utf8name) const { napi_value key; napi_status status = napi_create_string_utf8(_env, utf8name, std::strlen(utf8name), &key); NAPI_MAYBE_THROW_IF_FAILED(_env, status, bool); return HasOwnProperty(key); } inline MaybeOrValue Object::HasOwnProperty( const std::string& utf8name) const { return HasOwnProperty(utf8name.c_str()); } inline MaybeOrValue Object::Get(napi_value key) const { napi_value result; napi_status status = napi_get_property(_env, _value, key, &result); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, Value(_env, result), Value); } inline MaybeOrValue Object::Get(Value key) const { napi_value result; napi_status status = napi_get_property(_env, _value, key, &result); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, Value(_env, result), Value); } inline MaybeOrValue Object::Get(const char* utf8name) const { napi_value result; napi_status status = napi_get_named_property(_env, _value, utf8name, &result); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, Value(_env, result), Value); } inline MaybeOrValue Object::Get(const std::string& utf8name) const { return Get(utf8name.c_str()); } template inline MaybeOrValue Object::Set(napi_value key, const ValueType& value) const { napi_status status = napi_set_property(_env, _value, key, Value::From(_env, value)); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool); } template inline MaybeOrValue Object::Set(Value key, const ValueType& value) const { napi_status status = napi_set_property(_env, _value, key, Value::From(_env, value)); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool); } template inline MaybeOrValue Object::Set(const char* utf8name, const ValueType& value) const { napi_status status = napi_set_named_property(_env, _value, utf8name, Value::From(_env, value)); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool); } template inline MaybeOrValue Object::Set(const std::string& utf8name, const ValueType& value) const { return Set(utf8name.c_str(), value); } inline MaybeOrValue Object::Delete(napi_value key) const { bool result; napi_status status = napi_delete_property(_env, _value, key, &result); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool); } inline MaybeOrValue Object::Delete(Value key) const { bool result; napi_status status = napi_delete_property(_env, _value, key, &result); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool); } inline MaybeOrValue Object::Delete(const char* utf8name) const { return Delete(String::New(_env, utf8name)); } inline MaybeOrValue Object::Delete(const std::string& utf8name) const { return Delete(String::New(_env, utf8name)); } inline MaybeOrValue Object::Has(uint32_t index) const { bool result; napi_status status = napi_has_element(_env, _value, index, &result); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool); } inline MaybeOrValue Object::Get(uint32_t index) const { napi_value value; napi_status status = napi_get_element(_env, _value, index, &value); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, Value(_env, value), Value); } template inline MaybeOrValue Object::Set(uint32_t index, const ValueType& value) const { napi_status status = napi_set_element(_env, _value, index, Value::From(_env, value)); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool); } inline MaybeOrValue Object::Delete(uint32_t index) const { bool result; napi_status status = napi_delete_element(_env, _value, index, &result); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool); } inline MaybeOrValue Object::GetPropertyNames() const { napi_value result; napi_status status = napi_get_property_names(_env, _value, &result); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, Array(_env, result), Array); } inline MaybeOrValue Object::DefineProperty( const PropertyDescriptor& property) const { napi_status status = napi_define_properties( _env, _value, 1, reinterpret_cast(&property)); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool); } inline MaybeOrValue Object::DefineProperties( const std::initializer_list& properties) const { napi_status status = napi_define_properties( _env, _value, properties.size(), reinterpret_cast(properties.begin())); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool); } inline MaybeOrValue Object::DefineProperties( const std::vector& properties) const { napi_status status = napi_define_properties( _env, _value, properties.size(), reinterpret_cast(properties.data())); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool); } inline MaybeOrValue Object::InstanceOf( const Function& constructor) const { bool result; napi_status status = napi_instanceof(_env, _value, constructor, &result); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, result, bool); } template inline void Object::AddFinalizer(Finalizer finalizeCallback, T* data) const { details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), nullptr}); napi_status status = details::AttachData::Wrapper>( _env, *this, data, finalizeData); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED_VOID(_env, status); } } template inline void Object::AddFinalizer(Finalizer finalizeCallback, T* data, Hint* finalizeHint) const { details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), finalizeHint}); napi_status status = details:: AttachData::WrapperWithHint>( _env, *this, data, finalizeData); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED_VOID(_env, status); } } #ifdef NODE_ADDON_API_CPP_EXCEPTIONS inline Object::const_iterator::const_iterator(const Object* object, const Type type) { _object = object; _keys = object->GetPropertyNames(); _index = type == Type::BEGIN ? 0 : _keys.Length(); } inline Object::const_iterator Napi::Object::begin() const { const_iterator it(this, Object::const_iterator::Type::BEGIN); return it; } inline Object::const_iterator Napi::Object::end() const { const_iterator it(this, Object::const_iterator::Type::END); return it; } inline Object::const_iterator& Object::const_iterator::operator++() { ++_index; return *this; } inline bool Object::const_iterator::operator==( const const_iterator& other) const { return _index == other._index; } inline bool Object::const_iterator::operator!=( const const_iterator& other) const { return _index != other._index; } inline const std::pair> Object::const_iterator::operator*() const { const Value key = _keys[_index]; const PropertyLValue value = (*_object)[key]; return {key, value}; } inline Object::iterator::iterator(Object* object, const Type type) { _object = object; _keys = object->GetPropertyNames(); _index = type == Type::BEGIN ? 0 : _keys.Length(); } inline Object::iterator Napi::Object::begin() { iterator it(this, Object::iterator::Type::BEGIN); return it; } inline Object::iterator Napi::Object::end() { iterator it(this, Object::iterator::Type::END); return it; } inline Object::iterator& Object::iterator::operator++() { ++_index; return *this; } inline bool Object::iterator::operator==(const iterator& other) const { return _index == other._index; } inline bool Object::iterator::operator!=(const iterator& other) const { return _index != other._index; } inline std::pair> Object::iterator::operator*() { Value key = _keys[_index]; PropertyLValue value = (*_object)[key]; return {key, value}; } #endif // NODE_ADDON_API_CPP_EXCEPTIONS #if NAPI_VERSION >= 8 inline MaybeOrValue Object::Freeze() const { napi_status status = napi_object_freeze(_env, _value); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool); } inline MaybeOrValue Object::Seal() const { napi_status status = napi_object_seal(_env, _value); NAPI_RETURN_OR_THROW_IF_FAILED(_env, status, status == napi_ok, bool); } #endif // NAPI_VERSION >= 8 //////////////////////////////////////////////////////////////////////////////// // External class //////////////////////////////////////////////////////////////////////////////// template inline External External::New(napi_env env, T* data) { napi_value value; napi_status status = napi_create_external(env, data, nullptr, nullptr, &value); NAPI_THROW_IF_FAILED(env, status, External()); return External(env, value); } template template inline External External::New(napi_env env, T* data, Finalizer finalizeCallback) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), nullptr}); napi_status status = napi_create_external(env, data, details::FinalizeData::Wrapper, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, External()); } return External(env, value); } template template inline External External::New(napi_env env, T* data, Finalizer finalizeCallback, Hint* finalizeHint) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), finalizeHint}); napi_status status = napi_create_external( env, data, details::FinalizeData::WrapperWithHint, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, External()); } return External(env, value); } template inline void External::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "External::CheckCast", "empty value"); napi_valuetype type; napi_status status = napi_typeof(env, value, &type); NAPI_CHECK(status == napi_ok, "External::CheckCast", "napi_typeof failed"); NAPI_INTERNAL_CHECK_EQ(type, napi_external, "%d", "External::CheckCast"); } template inline External::External() : TypeTaggable() {} template inline External::External(napi_env env, napi_value value) : TypeTaggable(env, value) {} template inline T* External::Data() const { void* data; napi_status status = napi_get_value_external(_env, _value, &data); NAPI_THROW_IF_FAILED(_env, status, nullptr); return reinterpret_cast(data); } //////////////////////////////////////////////////////////////////////////////// // Array class //////////////////////////////////////////////////////////////////////////////// inline Array Array::New(napi_env env) { napi_value value; napi_status status = napi_create_array(env, &value); NAPI_THROW_IF_FAILED(env, status, Array()); return Array(env, value); } inline Array Array::New(napi_env env, size_t length) { napi_value value; napi_status status = napi_create_array_with_length(env, length, &value); NAPI_THROW_IF_FAILED(env, status, Array()); return Array(env, value); } inline void Array::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "Array::CheckCast", "empty value"); bool result; napi_status status = napi_is_array(env, value, &result); NAPI_CHECK(status == napi_ok, "Array::CheckCast", "napi_is_array failed"); NAPI_CHECK(result, "Array::CheckCast", "value is not array"); } inline Array::Array() : Object() {} inline Array::Array(napi_env env, napi_value value) : Object(env, value) {} inline uint32_t Array::Length() const { uint32_t result; napi_status status = napi_get_array_length(_env, _value, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } //////////////////////////////////////////////////////////////////////////////// // ArrayBuffer class //////////////////////////////////////////////////////////////////////////////// inline ArrayBuffer ArrayBuffer::New(napi_env env, size_t byteLength) { napi_value value; void* data; napi_status status = napi_create_arraybuffer(env, byteLength, &data, &value); NAPI_THROW_IF_FAILED(env, status, ArrayBuffer()); return ArrayBuffer(env, value); } #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED inline ArrayBuffer ArrayBuffer::New(napi_env env, void* externalData, size_t byteLength) { napi_value value; napi_status status = napi_create_external_arraybuffer( env, externalData, byteLength, nullptr, nullptr, &value); NAPI_THROW_IF_FAILED(env, status, ArrayBuffer()); return ArrayBuffer(env, value); } template inline ArrayBuffer ArrayBuffer::New(napi_env env, void* externalData, size_t byteLength, Finalizer finalizeCallback) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), nullptr}); napi_status status = napi_create_external_arraybuffer( env, externalData, byteLength, details::FinalizeData::Wrapper, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, ArrayBuffer()); } return ArrayBuffer(env, value); } template inline ArrayBuffer ArrayBuffer::New(napi_env env, void* externalData, size_t byteLength, Finalizer finalizeCallback, Hint* finalizeHint) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), finalizeHint}); napi_status status = napi_create_external_arraybuffer( env, externalData, byteLength, details::FinalizeData::WrapperWithHint, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, ArrayBuffer()); } return ArrayBuffer(env, value); } #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED inline void ArrayBuffer::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "ArrayBuffer::CheckCast", "empty value"); bool result; napi_status status = napi_is_arraybuffer(env, value, &result); NAPI_CHECK(status == napi_ok, "ArrayBuffer::CheckCast", "napi_is_arraybuffer failed"); NAPI_CHECK(result, "ArrayBuffer::CheckCast", "value is not arraybuffer"); } inline ArrayBuffer::ArrayBuffer() : Object() {} inline ArrayBuffer::ArrayBuffer(napi_env env, napi_value value) : Object(env, value) {} inline void* ArrayBuffer::Data() { void* data; napi_status status = napi_get_arraybuffer_info(_env, _value, &data, nullptr); NAPI_THROW_IF_FAILED(_env, status, nullptr); return data; } inline size_t ArrayBuffer::ByteLength() { size_t length; napi_status status = napi_get_arraybuffer_info(_env, _value, nullptr, &length); NAPI_THROW_IF_FAILED(_env, status, 0); return length; } #if NAPI_VERSION >= 7 inline bool ArrayBuffer::IsDetached() const { bool detached; napi_status status = napi_is_detached_arraybuffer(_env, _value, &detached); NAPI_THROW_IF_FAILED(_env, status, false); return detached; } inline void ArrayBuffer::Detach() { napi_status status = napi_detach_arraybuffer(_env, _value); NAPI_THROW_IF_FAILED_VOID(_env, status); } #endif // NAPI_VERSION >= 7 //////////////////////////////////////////////////////////////////////////////// // DataView class //////////////////////////////////////////////////////////////////////////////// inline DataView DataView::New(napi_env env, Napi::ArrayBuffer arrayBuffer) { return New(env, arrayBuffer, 0, arrayBuffer.ByteLength()); } inline DataView DataView::New(napi_env env, Napi::ArrayBuffer arrayBuffer, size_t byteOffset) { if (byteOffset > arrayBuffer.ByteLength()) { NAPI_THROW(RangeError::New( env, "Start offset is outside the bounds of the buffer"), DataView()); } return New( env, arrayBuffer, byteOffset, arrayBuffer.ByteLength() - byteOffset); } inline DataView DataView::New(napi_env env, Napi::ArrayBuffer arrayBuffer, size_t byteOffset, size_t byteLength) { if (byteOffset + byteLength > arrayBuffer.ByteLength()) { NAPI_THROW(RangeError::New(env, "Invalid DataView length"), DataView()); } napi_value value; napi_status status = napi_create_dataview(env, byteLength, arrayBuffer, byteOffset, &value); NAPI_THROW_IF_FAILED(env, status, DataView()); return DataView(env, value); } inline void DataView::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "DataView::CheckCast", "empty value"); bool result; napi_status status = napi_is_dataview(env, value, &result); NAPI_CHECK( status == napi_ok, "DataView::CheckCast", "napi_is_dataview failed"); NAPI_CHECK(result, "DataView::CheckCast", "value is not dataview"); } inline DataView::DataView() : Object() {} inline DataView::DataView(napi_env env, napi_value value) : Object(env, value) { napi_status status = napi_get_dataview_info(_env, _value /* dataView */, &_length /* byteLength */, &_data /* data */, nullptr /* arrayBuffer */, nullptr /* byteOffset */); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline Napi::ArrayBuffer DataView::ArrayBuffer() const { napi_value arrayBuffer; napi_status status = napi_get_dataview_info(_env, _value /* dataView */, nullptr /* byteLength */, nullptr /* data */, &arrayBuffer /* arrayBuffer */, nullptr /* byteOffset */); NAPI_THROW_IF_FAILED(_env, status, Napi::ArrayBuffer()); return Napi::ArrayBuffer(_env, arrayBuffer); } inline size_t DataView::ByteOffset() const { size_t byteOffset; napi_status status = napi_get_dataview_info(_env, _value /* dataView */, nullptr /* byteLength */, nullptr /* data */, nullptr /* arrayBuffer */, &byteOffset /* byteOffset */); NAPI_THROW_IF_FAILED(_env, status, 0); return byteOffset; } inline size_t DataView::ByteLength() const { return _length; } inline void* DataView::Data() const { return _data; } inline float DataView::GetFloat32(size_t byteOffset) const { return ReadData(byteOffset); } inline double DataView::GetFloat64(size_t byteOffset) const { return ReadData(byteOffset); } inline int8_t DataView::GetInt8(size_t byteOffset) const { return ReadData(byteOffset); } inline int16_t DataView::GetInt16(size_t byteOffset) const { return ReadData(byteOffset); } inline int32_t DataView::GetInt32(size_t byteOffset) const { return ReadData(byteOffset); } inline uint8_t DataView::GetUint8(size_t byteOffset) const { return ReadData(byteOffset); } inline uint16_t DataView::GetUint16(size_t byteOffset) const { return ReadData(byteOffset); } inline uint32_t DataView::GetUint32(size_t byteOffset) const { return ReadData(byteOffset); } inline void DataView::SetFloat32(size_t byteOffset, float value) const { WriteData(byteOffset, value); } inline void DataView::SetFloat64(size_t byteOffset, double value) const { WriteData(byteOffset, value); } inline void DataView::SetInt8(size_t byteOffset, int8_t value) const { WriteData(byteOffset, value); } inline void DataView::SetInt16(size_t byteOffset, int16_t value) const { WriteData(byteOffset, value); } inline void DataView::SetInt32(size_t byteOffset, int32_t value) const { WriteData(byteOffset, value); } inline void DataView::SetUint8(size_t byteOffset, uint8_t value) const { WriteData(byteOffset, value); } inline void DataView::SetUint16(size_t byteOffset, uint16_t value) const { WriteData(byteOffset, value); } inline void DataView::SetUint32(size_t byteOffset, uint32_t value) const { WriteData(byteOffset, value); } template inline T DataView::ReadData(size_t byteOffset) const { if (byteOffset + sizeof(T) > _length || byteOffset + sizeof(T) < byteOffset) { // overflow NAPI_THROW( RangeError::New(_env, "Offset is outside the bounds of the DataView"), 0); } return *reinterpret_cast(static_cast(_data) + byteOffset); } template inline void DataView::WriteData(size_t byteOffset, T value) const { if (byteOffset + sizeof(T) > _length || byteOffset + sizeof(T) < byteOffset) { // overflow NAPI_THROW_VOID( RangeError::New(_env, "Offset is outside the bounds of the DataView")); } *reinterpret_cast(static_cast(_data) + byteOffset) = value; } //////////////////////////////////////////////////////////////////////////////// // TypedArray class //////////////////////////////////////////////////////////////////////////////// inline void TypedArray::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "TypedArray::CheckCast", "empty value"); bool result; napi_status status = napi_is_typedarray(env, value, &result); NAPI_CHECK( status == napi_ok, "TypedArray::CheckCast", "napi_is_typedarray failed"); NAPI_CHECK(result, "TypedArray::CheckCast", "value is not typedarray"); } inline TypedArray::TypedArray() : Object(), _type(napi_typedarray_type::napi_int8_array), _length(0) {} inline TypedArray::TypedArray(napi_env env, napi_value value) : Object(env, value), _type(napi_typedarray_type::napi_int8_array), _length(0) { if (value != nullptr) { napi_status status = napi_get_typedarray_info(_env, _value, &const_cast(this)->_type, &const_cast(this)->_length, nullptr, nullptr, nullptr); NAPI_THROW_IF_FAILED_VOID(_env, status); } } inline TypedArray::TypedArray(napi_env env, napi_value value, napi_typedarray_type type, size_t length) : Object(env, value), _type(type), _length(length) {} inline napi_typedarray_type TypedArray::TypedArrayType() const { return _type; } inline uint8_t TypedArray::ElementSize() const { switch (_type) { case napi_int8_array: case napi_uint8_array: case napi_uint8_clamped_array: return 1; case napi_int16_array: case napi_uint16_array: return 2; case napi_int32_array: case napi_uint32_array: case napi_float32_array: return 4; case napi_float64_array: #if (NAPI_VERSION > 5) case napi_bigint64_array: case napi_biguint64_array: #endif // (NAPI_VERSION > 5) return 8; default: return 0; } } inline size_t TypedArray::ElementLength() const { return _length; } inline size_t TypedArray::ByteOffset() const { size_t byteOffset; napi_status status = napi_get_typedarray_info( _env, _value, nullptr, nullptr, nullptr, nullptr, &byteOffset); NAPI_THROW_IF_FAILED(_env, status, 0); return byteOffset; } inline size_t TypedArray::ByteLength() const { return ElementSize() * ElementLength(); } inline Napi::ArrayBuffer TypedArray::ArrayBuffer() const { napi_value arrayBuffer; napi_status status = napi_get_typedarray_info( _env, _value, nullptr, nullptr, nullptr, &arrayBuffer, nullptr); NAPI_THROW_IF_FAILED(_env, status, Napi::ArrayBuffer()); return Napi::ArrayBuffer(_env, arrayBuffer); } //////////////////////////////////////////////////////////////////////////////// // TypedArrayOf class //////////////////////////////////////////////////////////////////////////////// template inline void TypedArrayOf::CheckCast(napi_env env, napi_value value) { TypedArray::CheckCast(env, value); napi_typedarray_type type; napi_status status = napi_get_typedarray_info( env, value, &type, nullptr, nullptr, nullptr, nullptr); NAPI_CHECK(status == napi_ok, "TypedArrayOf::CheckCast", "napi_is_typedarray failed"); NAPI_INTERNAL_CHECK( (type == TypedArrayTypeForPrimitiveType() || (type == napi_uint8_clamped_array && std::is_same::value)), "TypedArrayOf::CheckCast", "Array type must match the template parameter, (Uint8 arrays may " "optionally have the \"clamped\" array type.), got %d.", type); } template inline TypedArrayOf TypedArrayOf::New(napi_env env, size_t elementLength, napi_typedarray_type type) { Napi::ArrayBuffer arrayBuffer = Napi::ArrayBuffer::New(env, elementLength * sizeof(T)); return New(env, elementLength, arrayBuffer, 0, type); } template inline TypedArrayOf TypedArrayOf::New(napi_env env, size_t elementLength, Napi::ArrayBuffer arrayBuffer, size_t bufferOffset, napi_typedarray_type type) { napi_value value; napi_status status = napi_create_typedarray( env, type, elementLength, arrayBuffer, bufferOffset, &value); NAPI_THROW_IF_FAILED(env, status, TypedArrayOf()); return TypedArrayOf( env, value, type, elementLength, reinterpret_cast(reinterpret_cast(arrayBuffer.Data()) + bufferOffset)); } template inline TypedArrayOf::TypedArrayOf() : TypedArray(), _data(nullptr) {} template inline TypedArrayOf::TypedArrayOf(napi_env env, napi_value value) : TypedArray(env, value), _data(nullptr) { napi_status status = napi_ok; if (value != nullptr) { void* data = nullptr; status = napi_get_typedarray_info( _env, _value, &_type, &_length, &data, nullptr, nullptr); _data = static_cast(data); } else { _type = TypedArrayTypeForPrimitiveType(); _length = 0; } NAPI_THROW_IF_FAILED_VOID(_env, status); } template inline TypedArrayOf::TypedArrayOf(napi_env env, napi_value value, napi_typedarray_type type, size_t length, T* data) : TypedArray(env, value, type, length), _data(data) { if (!(type == TypedArrayTypeForPrimitiveType() || (type == napi_uint8_clamped_array && std::is_same::value))) { NAPI_THROW_VOID(TypeError::New( env, "Array type must match the template parameter. " "(Uint8 arrays may optionally have the \"clamped\" array type.)")); } } template inline T& TypedArrayOf::operator[](size_t index) { return _data[index]; } template inline const T& TypedArrayOf::operator[](size_t index) const { return _data[index]; } template inline T* TypedArrayOf::Data() { return _data; } template inline const T* TypedArrayOf::Data() const { return _data; } //////////////////////////////////////////////////////////////////////////////// // Function class //////////////////////////////////////////////////////////////////////////////// template inline napi_status CreateFunction(napi_env env, const char* utf8name, napi_callback cb, CbData* data, napi_value* result) { napi_status status = napi_create_function(env, utf8name, NAPI_AUTO_LENGTH, cb, data, result); if (status == napi_ok) { status = Napi::details::AttachData(env, *result, data); } return status; } template inline Function Function::New(napi_env env, const char* utf8name, void* data) { napi_value result = nullptr; napi_status status = napi_create_function(env, utf8name, NAPI_AUTO_LENGTH, details::TemplatedVoidCallback, data, &result); NAPI_THROW_IF_FAILED(env, status, Function()); return Function(env, result); } template inline Function Function::New(napi_env env, const char* utf8name, void* data) { napi_value result = nullptr; napi_status status = napi_create_function(env, utf8name, NAPI_AUTO_LENGTH, details::TemplatedCallback, data, &result); NAPI_THROW_IF_FAILED(env, status, Function()); return Function(env, result); } template inline Function Function::New(napi_env env, const std::string& utf8name, void* data) { return Function::New(env, utf8name.c_str(), data); } template inline Function Function::New(napi_env env, const std::string& utf8name, void* data) { return Function::New(env, utf8name.c_str(), data); } template inline Function Function::New(napi_env env, Callable cb, const char* utf8name, void* data) { using ReturnType = decltype(cb(CallbackInfo(nullptr, nullptr))); using CbData = details::CallbackData; auto callbackData = new CbData{std::move(cb), data}; napi_value value; napi_status status = CreateFunction(env, utf8name, CbData::Wrapper, callbackData, &value); if (status != napi_ok) { delete callbackData; NAPI_THROW_IF_FAILED(env, status, Function()); } return Function(env, value); } template inline Function Function::New(napi_env env, Callable cb, const std::string& utf8name, void* data) { return New(env, cb, utf8name.c_str(), data); } inline void Function::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "Function::CheckCast", "empty value"); napi_valuetype type; napi_status status = napi_typeof(env, value, &type); NAPI_CHECK(status == napi_ok, "Function::CheckCast", "napi_typeof failed"); NAPI_INTERNAL_CHECK_EQ(type, napi_function, "%d", "Function::CheckCast"); } inline Function::Function() : Object() {} inline Function::Function(napi_env env, napi_value value) : Object(env, value) {} inline MaybeOrValue Function::operator()( const std::initializer_list& args) const { return Call(Env().Undefined(), args); } inline MaybeOrValue Function::Call( const std::initializer_list& args) const { return Call(Env().Undefined(), args); } inline MaybeOrValue Function::Call( const std::vector& args) const { return Call(Env().Undefined(), args); } inline MaybeOrValue Function::Call( const std::vector& args) const { return Call(Env().Undefined(), args); } inline MaybeOrValue Function::Call(size_t argc, const napi_value* args) const { return Call(Env().Undefined(), argc, args); } inline MaybeOrValue Function::Call( napi_value recv, const std::initializer_list& args) const { return Call(recv, args.size(), args.begin()); } inline MaybeOrValue Function::Call( napi_value recv, const std::vector& args) const { return Call(recv, args.size(), args.data()); } inline MaybeOrValue Function::Call( napi_value recv, const std::vector& args) const { const size_t argc = args.size(); const size_t stackArgsCount = 6; napi_value stackArgs[stackArgsCount]; std::vector heapArgs; napi_value* argv; if (argc <= stackArgsCount) { argv = stackArgs; } else { heapArgs.resize(argc); argv = heapArgs.data(); } for (size_t index = 0; index < argc; index++) { argv[index] = static_cast(args[index]); } return Call(recv, argc, argv); } inline MaybeOrValue Function::Call(napi_value recv, size_t argc, const napi_value* args) const { napi_value result; napi_status status = napi_call_function(_env, recv, _value, argc, args, &result); NAPI_RETURN_OR_THROW_IF_FAILED( _env, status, Napi::Value(_env, result), Napi::Value); } inline MaybeOrValue Function::MakeCallback( napi_value recv, const std::initializer_list& args, napi_async_context context) const { return MakeCallback(recv, args.size(), args.begin(), context); } inline MaybeOrValue Function::MakeCallback( napi_value recv, const std::vector& args, napi_async_context context) const { return MakeCallback(recv, args.size(), args.data(), context); } inline MaybeOrValue Function::MakeCallback( napi_value recv, size_t argc, const napi_value* args, napi_async_context context) const { napi_value result; napi_status status = napi_make_callback(_env, context, recv, _value, argc, args, &result); NAPI_RETURN_OR_THROW_IF_FAILED( _env, status, Napi::Value(_env, result), Napi::Value); } inline MaybeOrValue Function::New( const std::initializer_list& args) const { return New(args.size(), args.begin()); } inline MaybeOrValue Function::New( const std::vector& args) const { return New(args.size(), args.data()); } inline MaybeOrValue Function::New(size_t argc, const napi_value* args) const { napi_value result; napi_status status = napi_new_instance(_env, _value, argc, args, &result); NAPI_RETURN_OR_THROW_IF_FAILED( _env, status, Napi::Object(_env, result), Napi::Object); } //////////////////////////////////////////////////////////////////////////////// // Promise class //////////////////////////////////////////////////////////////////////////////// inline Promise::Deferred Promise::Deferred::New(napi_env env) { return Promise::Deferred(env); } inline Promise::Deferred::Deferred(napi_env env) : _env(env) { napi_status status = napi_create_promise(_env, &_deferred, &_promise); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline Promise Promise::Deferred::Promise() const { return Napi::Promise(_env, _promise); } inline Napi::Env Promise::Deferred::Env() const { return Napi::Env(_env); } inline void Promise::Deferred::Resolve(napi_value value) const { napi_status status = napi_resolve_deferred(_env, _deferred, value); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline void Promise::Deferred::Reject(napi_value value) const { napi_status status = napi_reject_deferred(_env, _deferred, value); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline void Promise::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "Promise::CheckCast", "empty value"); bool result; napi_status status = napi_is_promise(env, value, &result); NAPI_CHECK(status == napi_ok, "Promise::CheckCast", "napi_is_promise failed"); NAPI_CHECK(result, "Promise::CheckCast", "value is not promise"); } inline Promise::Promise(napi_env env, napi_value value) : Object(env, value) {} //////////////////////////////////////////////////////////////////////////////// // Buffer class //////////////////////////////////////////////////////////////////////////////// template inline Buffer Buffer::New(napi_env env, size_t length) { napi_value value; void* data; napi_status status = napi_create_buffer(env, length * sizeof(T), &data, &value); NAPI_THROW_IF_FAILED(env, status, Buffer()); return Buffer(env, value); } #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED template inline Buffer Buffer::New(napi_env env, T* data, size_t length) { napi_value value; napi_status status = napi_create_external_buffer( env, length * sizeof(T), data, nullptr, nullptr, &value); NAPI_THROW_IF_FAILED(env, status, Buffer()); return Buffer(env, value); } template template inline Buffer Buffer::New(napi_env env, T* data, size_t length, Finalizer finalizeCallback) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), nullptr}); napi_status status = napi_create_external_buffer(env, length * sizeof(T), data, details::FinalizeData::Wrapper, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, Buffer()); } return Buffer(env, value); } template template inline Buffer Buffer::New(napi_env env, T* data, size_t length, Finalizer finalizeCallback, Hint* finalizeHint) { napi_value value; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), finalizeHint}); napi_status status = napi_create_external_buffer( env, length * sizeof(T), data, details::FinalizeData::WrapperWithHint, finalizeData, &value); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, Buffer()); } return Buffer(env, value); } #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED template inline Buffer Buffer::NewOrCopy(napi_env env, T* data, size_t length) { #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED napi_value value; napi_status status = napi_create_external_buffer( env, length * sizeof(T), data, nullptr, nullptr, &value); if (status == details::napi_no_external_buffers_allowed) { #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED // If we can't create an external buffer, we'll just copy the data. return Buffer::Copy(env, data, length); #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED } NAPI_THROW_IF_FAILED(env, status, Buffer()); return Buffer(env, value); #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED } template template inline Buffer Buffer::NewOrCopy(napi_env env, T* data, size_t length, Finalizer finalizeCallback) { details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), nullptr}); #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED napi_value value; napi_status status = napi_create_external_buffer(env, length * sizeof(T), data, details::FinalizeData::Wrapper, finalizeData, &value); if (status == details::napi_no_external_buffers_allowed) { #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED // If we can't create an external buffer, we'll just copy the data. Buffer ret = Buffer::Copy(env, data, length); details::FinalizeData::WrapperGC(env, data, finalizeData); return ret; #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED } if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, Buffer()); } return Buffer(env, value); #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED } template template inline Buffer Buffer::NewOrCopy(napi_env env, T* data, size_t length, Finalizer finalizeCallback, Hint* finalizeHint) { details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), finalizeHint}); #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED napi_value value; napi_status status = napi_create_external_buffer( env, length * sizeof(T), data, details::FinalizeData::WrapperWithHint, finalizeData, &value); if (status == details::napi_no_external_buffers_allowed) { #endif // If we can't create an external buffer, we'll just copy the data. Buffer ret = Buffer::Copy(env, data, length); details::FinalizeData::WrapperGCWithHint( env, data, finalizeData); return ret; #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED } if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, Buffer()); } return Buffer(env, value); #endif } template inline Buffer Buffer::Copy(napi_env env, const T* data, size_t length) { napi_value value; napi_status status = napi_create_buffer_copy(env, length * sizeof(T), data, nullptr, &value); NAPI_THROW_IF_FAILED(env, status, Buffer()); return Buffer(env, value); } template inline void Buffer::CheckCast(napi_env env, napi_value value) { NAPI_CHECK(value != nullptr, "Buffer::CheckCast", "empty value"); bool result; napi_status status = napi_is_buffer(env, value, &result); NAPI_CHECK(status == napi_ok, "Buffer::CheckCast", "napi_is_buffer failed"); NAPI_CHECK(result, "Buffer::CheckCast", "value is not buffer"); } template inline Buffer::Buffer() : Uint8Array() {} template inline Buffer::Buffer(napi_env env, napi_value value) : Uint8Array(env, value) {} template inline size_t Buffer::Length() const { return ByteLength() / sizeof(T); } template inline T* Buffer::Data() const { return reinterpret_cast(const_cast(Uint8Array::Data())); } //////////////////////////////////////////////////////////////////////////////// // Error class //////////////////////////////////////////////////////////////////////////////// inline Error Error::New(napi_env env) { napi_status status; napi_value error = nullptr; bool is_exception_pending; napi_extended_error_info last_error_info_copy; { // We must retrieve the last error info before doing anything else because // doing anything else will replace the last error info. const napi_extended_error_info* last_error_info; status = napi_get_last_error_info(env, &last_error_info); NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_get_last_error_info"); // All fields of the `napi_extended_error_info` structure gets reset in // subsequent Node-API function calls on the same `env`. This includes a // call to `napi_is_exception_pending()`. So here it is necessary to make a // copy of the information as the `error_code` field is used later on. memcpy(&last_error_info_copy, last_error_info, sizeof(napi_extended_error_info)); } status = napi_is_exception_pending(env, &is_exception_pending); NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_is_exception_pending"); // A pending exception takes precedence over any internal error status. if (is_exception_pending) { status = napi_get_and_clear_last_exception(env, &error); NAPI_FATAL_IF_FAILED( status, "Error::New", "napi_get_and_clear_last_exception"); } else { const char* error_message = last_error_info_copy.error_message != nullptr ? last_error_info_copy.error_message : "Error in native callback"; napi_value message; status = napi_create_string_utf8( env, error_message, std::strlen(error_message), &message); NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_create_string_utf8"); switch (last_error_info_copy.error_code) { case napi_object_expected: case napi_string_expected: case napi_boolean_expected: case napi_number_expected: status = napi_create_type_error(env, nullptr, message, &error); break; default: status = napi_create_error(env, nullptr, message, &error); break; } NAPI_FATAL_IF_FAILED(status, "Error::New", "napi_create_error"); } return Error(env, error); } inline Error Error::New(napi_env env, const char* message) { return Error::New( env, message, std::strlen(message), napi_create_error); } inline Error Error::New(napi_env env, const std::string& message) { return Error::New( env, message.c_str(), message.size(), napi_create_error); } inline NAPI_NO_RETURN void Error::Fatal(const char* location, const char* message) { napi_fatal_error(location, NAPI_AUTO_LENGTH, message, NAPI_AUTO_LENGTH); } inline Error::Error() : ObjectReference() {} inline Error::Error(napi_env env, napi_value value) : ObjectReference(env, nullptr) { if (value != nullptr) { // Attempting to create a reference on the error object. // If it's not a Object/Function/Symbol, this call will return an error // status. napi_status status = napi_create_reference(env, value, 1, &_ref); if (status != napi_ok) { napi_value wrappedErrorObj; // Create an error object status = napi_create_object(env, &wrappedErrorObj); NAPI_FATAL_IF_FAILED(status, "Error::Error", "napi_create_object"); // property flag that we attach to show the error object is wrapped napi_property_descriptor wrapObjFlag = { ERROR_WRAP_VALUE(), // Unique GUID identifier since Symbol isn't a // viable option nullptr, nullptr, nullptr, nullptr, Value::From(env, value), napi_enumerable, nullptr}; status = napi_define_properties(env, wrappedErrorObj, 1, &wrapObjFlag); #ifdef NODE_API_SWALLOW_UNTHROWABLE_EXCEPTIONS if (status == napi_pending_exception) { // Test if the pending exception was reported because the environment is // shutting down. We assume that a status of napi_pending_exception // coupled with the absence of an actual pending exception means that // the environment is shutting down. If so, we replace the // napi_pending_exception status with napi_ok. bool is_exception_pending = false; status = napi_is_exception_pending(env, &is_exception_pending); if (status == napi_ok && !is_exception_pending) { status = napi_ok; } else { status = napi_pending_exception; } } #endif // NODE_API_SWALLOW_UNTHROWABLE_EXCEPTIONS NAPI_FATAL_IF_FAILED(status, "Error::Error", "napi_define_properties"); // Create a reference on the newly wrapped object status = napi_create_reference(env, wrappedErrorObj, 1, &_ref); } // Avoid infinite recursion in the failure case. NAPI_FATAL_IF_FAILED(status, "Error::Error", "napi_create_reference"); } } inline Object Error::Value() const { if (_ref == nullptr) { return Object(_env, nullptr); } napi_value refValue; napi_status status = napi_get_reference_value(_env, _ref, &refValue); NAPI_THROW_IF_FAILED(_env, status, Object()); napi_valuetype type; status = napi_typeof(_env, refValue, &type); NAPI_THROW_IF_FAILED(_env, status, Object()); // If refValue isn't a symbol, then we proceed to whether the refValue has the // wrapped error flag if (type != napi_symbol) { // We are checking if the object is wrapped bool isWrappedObject = false; status = napi_has_property(_env, refValue, String::From(_env, ERROR_WRAP_VALUE()), &isWrappedObject); // Don't care about status if (isWrappedObject) { napi_value unwrappedValue; status = napi_get_property(_env, refValue, String::From(_env, ERROR_WRAP_VALUE()), &unwrappedValue); NAPI_THROW_IF_FAILED(_env, status, Object()); return Object(_env, unwrappedValue); } } return Object(_env, refValue); } inline Error::Error(Error&& other) : ObjectReference(std::move(other)) {} inline Error& Error::operator=(Error&& other) { static_cast*>(this)->operator=(std::move(other)); return *this; } inline Error::Error(const Error& other) : ObjectReference(other) {} inline Error& Error::operator=(const Error& other) { Reset(); _env = other.Env(); HandleScope scope(_env); napi_value value = other.Value(); if (value != nullptr) { napi_status status = napi_create_reference(_env, value, 1, &_ref); NAPI_THROW_IF_FAILED(_env, status, *this); } return *this; } inline const std::string& Error::Message() const NAPI_NOEXCEPT { if (_message.size() == 0 && _env != nullptr) { #ifdef NODE_ADDON_API_CPP_EXCEPTIONS try { _message = Get("message").As(); } catch (...) { // Catch all errors here, to include e.g. a std::bad_alloc from // the std::string::operator=, because this method may not throw. } #else // NODE_ADDON_API_CPP_EXCEPTIONS #if defined(NODE_ADDON_API_ENABLE_MAYBE) Napi::Value message_val; if (Get("message").UnwrapTo(&message_val)) { _message = message_val.As(); } #else _message = Get("message").As(); #endif #endif // NODE_ADDON_API_CPP_EXCEPTIONS } return _message; } // we created an object on the &_ref inline void Error::ThrowAsJavaScriptException() const { HandleScope scope(_env); if (!IsEmpty()) { #ifdef NODE_API_SWALLOW_UNTHROWABLE_EXCEPTIONS bool pendingException = false; // check if there is already a pending exception. If so don't try to throw a // new one as that is not allowed/possible napi_status status = napi_is_exception_pending(_env, &pendingException); if ((status != napi_ok) || ((status == napi_ok) && (pendingException == false))) { // We intentionally don't use `NAPI_THROW_*` macros here to ensure // that there is no possible recursion as `ThrowAsJavaScriptException` // is part of `NAPI_THROW_*` macro definition for noexcept. status = napi_throw(_env, Value()); #ifdef NAPI_EXPERIMENTAL napi_status expected_failure_mode = napi_cannot_run_js; #else napi_status expected_failure_mode = napi_pending_exception; #endif if (status == expected_failure_mode) { // The environment must be terminating as we checked earlier and there // was no pending exception. In this case continuing will result // in a fatal error and there is nothing the author has done incorrectly // in their code that is worth flagging through a fatal error return; } } else { status = napi_pending_exception; } #else // We intentionally don't use `NAPI_THROW_*` macros here to ensure // that there is no possible recursion as `ThrowAsJavaScriptException` // is part of `NAPI_THROW_*` macro definition for noexcept. napi_status status = napi_throw(_env, Value()); #endif #ifdef NODE_ADDON_API_CPP_EXCEPTIONS if (status != napi_ok) { throw Error::New(_env); } #else // NODE_ADDON_API_CPP_EXCEPTIONS NAPI_FATAL_IF_FAILED( status, "Error::ThrowAsJavaScriptException", "napi_throw"); #endif // NODE_ADDON_API_CPP_EXCEPTIONS } } #ifdef NODE_ADDON_API_CPP_EXCEPTIONS inline const char* Error::what() const NAPI_NOEXCEPT { return Message().c_str(); } #endif // NODE_ADDON_API_CPP_EXCEPTIONS inline const char* Error::ERROR_WRAP_VALUE() NAPI_NOEXCEPT { return "4bda9e7e-4913-4dbc-95de-891cbf66598e-errorVal"; } template inline TError Error::New(napi_env env, const char* message, size_t length, create_error_fn create_error) { napi_value str; napi_status status = napi_create_string_utf8(env, message, length, &str); NAPI_THROW_IF_FAILED(env, status, TError()); napi_value error; status = create_error(env, nullptr, str, &error); NAPI_THROW_IF_FAILED(env, status, TError()); return TError(env, error); } inline TypeError TypeError::New(napi_env env, const char* message) { return Error::New( env, message, std::strlen(message), napi_create_type_error); } inline TypeError TypeError::New(napi_env env, const std::string& message) { return Error::New( env, message.c_str(), message.size(), napi_create_type_error); } inline TypeError::TypeError() : Error() {} inline TypeError::TypeError(napi_env env, napi_value value) : Error(env, value) {} inline RangeError RangeError::New(napi_env env, const char* message) { return Error::New( env, message, std::strlen(message), napi_create_range_error); } inline RangeError RangeError::New(napi_env env, const std::string& message) { return Error::New( env, message.c_str(), message.size(), napi_create_range_error); } inline RangeError::RangeError() : Error() {} inline RangeError::RangeError(napi_env env, napi_value value) : Error(env, value) {} #if NAPI_VERSION > 8 inline SyntaxError SyntaxError::New(napi_env env, const char* message) { return Error::New( env, message, std::strlen(message), node_api_create_syntax_error); } inline SyntaxError SyntaxError::New(napi_env env, const std::string& message) { return Error::New( env, message.c_str(), message.size(), node_api_create_syntax_error); } inline SyntaxError::SyntaxError() : Error() {} inline SyntaxError::SyntaxError(napi_env env, napi_value value) : Error(env, value) {} #endif // NAPI_VERSION > 8 //////////////////////////////////////////////////////////////////////////////// // Reference class //////////////////////////////////////////////////////////////////////////////// template inline Reference Reference::New(const T& value, uint32_t initialRefcount) { napi_env env = value.Env(); napi_value val = value; if (val == nullptr) { return Reference(env, nullptr); } napi_ref ref; napi_status status = napi_create_reference(env, value, initialRefcount, &ref); NAPI_THROW_IF_FAILED(env, status, Reference()); return Reference(env, ref); } template inline Reference::Reference() : _env(nullptr), _ref(nullptr), _suppressDestruct(false) {} template inline Reference::Reference(napi_env env, napi_ref ref) : _env(env), _ref(ref), _suppressDestruct(false) {} template inline Reference::~Reference() { if (_ref != nullptr) { if (!_suppressDestruct) { // TODO(legendecas): napi_delete_reference should be invoked immediately. // Fix this when https://github.com/nodejs/node/pull/55620 lands. #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER Env().PostFinalizer( [](Napi::Env env, napi_ref ref) { napi_delete_reference(env, ref); }, _ref); #else napi_delete_reference(_env, _ref); #endif } _ref = nullptr; } } template inline Reference::Reference(Reference&& other) : _env(other._env), _ref(other._ref), _suppressDestruct(other._suppressDestruct) { other._env = nullptr; other._ref = nullptr; other._suppressDestruct = false; } template inline Reference& Reference::operator=(Reference&& other) { Reset(); _env = other._env; _ref = other._ref; _suppressDestruct = other._suppressDestruct; other._env = nullptr; other._ref = nullptr; other._suppressDestruct = false; return *this; } template inline Reference::Reference(const Reference& other) : _env(other._env), _ref(nullptr), _suppressDestruct(false) { HandleScope scope(_env); napi_value value = other.Value(); if (value != nullptr) { // Copying is a limited scenario (currently only used for Error object) and // always creates a strong reference to the given value even if the incoming // reference is weak. napi_status status = napi_create_reference(_env, value, 1, &_ref); NAPI_FATAL_IF_FAILED( status, "Reference::Reference", "napi_create_reference"); } } template inline Reference::operator napi_ref() const { return _ref; } template inline bool Reference::operator==(const Reference& other) const { HandleScope scope(_env); return this->Value().StrictEquals(other.Value()); } template inline bool Reference::operator!=(const Reference& other) const { return !this->operator==(other); } template inline Napi::Env Reference::Env() const { return Napi::Env(_env); } template inline bool Reference::IsEmpty() const { return _ref == nullptr; } template inline T Reference::Value() const { if (_ref == nullptr) { return T(_env, nullptr); } napi_value value; napi_status status = napi_get_reference_value(_env, _ref, &value); NAPI_THROW_IF_FAILED(_env, status, T()); return T(_env, value); } template inline uint32_t Reference::Ref() const { uint32_t result; napi_status status = napi_reference_ref(_env, _ref, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } template inline uint32_t Reference::Unref() const { uint32_t result; napi_status status = napi_reference_unref(_env, _ref, &result); NAPI_THROW_IF_FAILED(_env, status, 0); return result; } template inline void Reference::Reset() { if (_ref != nullptr) { napi_status status = napi_delete_reference(_env, _ref); NAPI_THROW_IF_FAILED_VOID(_env, status); _ref = nullptr; } } template inline void Reference::Reset(const T& value, uint32_t refcount) { Reset(); _env = value.Env(); napi_value val = value; if (val != nullptr) { napi_status status = napi_create_reference(_env, value, refcount, &_ref); NAPI_THROW_IF_FAILED_VOID(_env, status); } } template inline void Reference::SuppressDestruct() { _suppressDestruct = true; } template inline Reference Weak(T value) { return Reference::New(value, 0); } inline ObjectReference Weak(Object value) { return Reference::New(value, 0); } inline FunctionReference Weak(Function value) { return Reference::New(value, 0); } template inline Reference Persistent(T value) { return Reference::New(value, 1); } inline ObjectReference Persistent(Object value) { return Reference::New(value, 1); } inline FunctionReference Persistent(Function value) { return Reference::New(value, 1); } //////////////////////////////////////////////////////////////////////////////// // ObjectReference class //////////////////////////////////////////////////////////////////////////////// inline ObjectReference::ObjectReference() : Reference() {} inline ObjectReference::ObjectReference(napi_env env, napi_ref ref) : Reference(env, ref) {} inline ObjectReference::ObjectReference(Reference&& other) : Reference(std::move(other)) {} inline ObjectReference& ObjectReference::operator=(Reference&& other) { static_cast*>(this)->operator=(std::move(other)); return *this; } inline ObjectReference::ObjectReference(ObjectReference&& other) : Reference(std::move(other)) {} inline ObjectReference& ObjectReference::operator=(ObjectReference&& other) { static_cast*>(this)->operator=(std::move(other)); return *this; } inline ObjectReference::ObjectReference(const ObjectReference& other) : Reference(other) {} inline MaybeOrValue ObjectReference::Get( const char* utf8name) const { EscapableHandleScope scope(_env); MaybeOrValue result = Value().Get(utf8name); #ifdef NODE_ADDON_API_ENABLE_MAYBE if (result.IsJust()) { return Just(scope.Escape(result.Unwrap())); } return result; #else if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); #endif } inline MaybeOrValue ObjectReference::Get( const std::string& utf8name) const { EscapableHandleScope scope(_env); MaybeOrValue result = Value().Get(utf8name); #ifdef NODE_ADDON_API_ENABLE_MAYBE if (result.IsJust()) { return Just(scope.Escape(result.Unwrap())); } return result; #else if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); #endif } inline MaybeOrValue ObjectReference::Set(const char* utf8name, napi_value value) const { HandleScope scope(_env); return Value().Set(utf8name, value); } inline MaybeOrValue ObjectReference::Set(const char* utf8name, Napi::Value value) const { HandleScope scope(_env); return Value().Set(utf8name, value); } inline MaybeOrValue ObjectReference::Set(const char* utf8name, const char* utf8value) const { HandleScope scope(_env); return Value().Set(utf8name, utf8value); } inline MaybeOrValue ObjectReference::Set(const char* utf8name, bool boolValue) const { HandleScope scope(_env); return Value().Set(utf8name, boolValue); } inline MaybeOrValue ObjectReference::Set(const char* utf8name, double numberValue) const { HandleScope scope(_env); return Value().Set(utf8name, numberValue); } inline MaybeOrValue ObjectReference::Set(const std::string& utf8name, napi_value value) const { HandleScope scope(_env); return Value().Set(utf8name, value); } inline MaybeOrValue ObjectReference::Set(const std::string& utf8name, Napi::Value value) const { HandleScope scope(_env); return Value().Set(utf8name, value); } inline MaybeOrValue ObjectReference::Set(const std::string& utf8name, std::string& utf8value) const { HandleScope scope(_env); return Value().Set(utf8name, utf8value); } inline MaybeOrValue ObjectReference::Set(const std::string& utf8name, bool boolValue) const { HandleScope scope(_env); return Value().Set(utf8name, boolValue); } inline MaybeOrValue ObjectReference::Set(const std::string& utf8name, double numberValue) const { HandleScope scope(_env); return Value().Set(utf8name, numberValue); } inline MaybeOrValue ObjectReference::Get(uint32_t index) const { EscapableHandleScope scope(_env); MaybeOrValue result = Value().Get(index); #ifdef NODE_ADDON_API_ENABLE_MAYBE if (result.IsJust()) { return Just(scope.Escape(result.Unwrap())); } return result; #else if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); #endif } inline MaybeOrValue ObjectReference::Set(uint32_t index, napi_value value) const { HandleScope scope(_env); return Value().Set(index, value); } inline MaybeOrValue ObjectReference::Set(uint32_t index, Napi::Value value) const { HandleScope scope(_env); return Value().Set(index, value); } inline MaybeOrValue ObjectReference::Set(uint32_t index, const char* utf8value) const { HandleScope scope(_env); return Value().Set(index, utf8value); } inline MaybeOrValue ObjectReference::Set( uint32_t index, const std::string& utf8value) const { HandleScope scope(_env); return Value().Set(index, utf8value); } inline MaybeOrValue ObjectReference::Set(uint32_t index, bool boolValue) const { HandleScope scope(_env); return Value().Set(index, boolValue); } inline MaybeOrValue ObjectReference::Set(uint32_t index, double numberValue) const { HandleScope scope(_env); return Value().Set(index, numberValue); } //////////////////////////////////////////////////////////////////////////////// // FunctionReference class //////////////////////////////////////////////////////////////////////////////// inline FunctionReference::FunctionReference() : Reference() {} inline FunctionReference::FunctionReference(napi_env env, napi_ref ref) : Reference(env, ref) {} inline FunctionReference::FunctionReference(Reference&& other) : Reference(std::move(other)) {} inline FunctionReference& FunctionReference::operator=( Reference&& other) { static_cast*>(this)->operator=(std::move(other)); return *this; } inline FunctionReference::FunctionReference(FunctionReference&& other) : Reference(std::move(other)) {} inline FunctionReference& FunctionReference::operator=( FunctionReference&& other) { static_cast*>(this)->operator=(std::move(other)); return *this; } inline MaybeOrValue FunctionReference::operator()( const std::initializer_list& args) const { EscapableHandleScope scope(_env); MaybeOrValue result = Value()(args); #ifdef NODE_ADDON_API_ENABLE_MAYBE if (result.IsJust()) { return Just(scope.Escape(result.Unwrap())); } return result; #else if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); #endif } inline MaybeOrValue FunctionReference::Call( const std::initializer_list& args) const { EscapableHandleScope scope(_env); MaybeOrValue result = Value().Call(args); #ifdef NODE_ADDON_API_ENABLE_MAYBE if (result.IsJust()) { return Just(scope.Escape(result.Unwrap())); } return result; #else if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); #endif } inline MaybeOrValue FunctionReference::Call( const std::vector& args) const { EscapableHandleScope scope(_env); MaybeOrValue result = Value().Call(args); #ifdef NODE_ADDON_API_ENABLE_MAYBE if (result.IsJust()) { return Just(scope.Escape(result.Unwrap())); } return result; #else if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); #endif } inline MaybeOrValue FunctionReference::Call( napi_value recv, const std::initializer_list& args) const { EscapableHandleScope scope(_env); MaybeOrValue result = Value().Call(recv, args); #ifdef NODE_ADDON_API_ENABLE_MAYBE if (result.IsJust()) { return Just(scope.Escape(result.Unwrap())); } return result; #else if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); #endif } inline MaybeOrValue FunctionReference::Call( napi_value recv, const std::vector& args) const { EscapableHandleScope scope(_env); MaybeOrValue result = Value().Call(recv, args); #ifdef NODE_ADDON_API_ENABLE_MAYBE if (result.IsJust()) { return Just(scope.Escape(result.Unwrap())); } return result; #else if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); #endif } inline MaybeOrValue FunctionReference::Call( napi_value recv, size_t argc, const napi_value* args) const { EscapableHandleScope scope(_env); MaybeOrValue result = Value().Call(recv, argc, args); #ifdef NODE_ADDON_API_ENABLE_MAYBE if (result.IsJust()) { return Just(scope.Escape(result.Unwrap())); } return result; #else if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); #endif } inline MaybeOrValue FunctionReference::MakeCallback( napi_value recv, const std::initializer_list& args, napi_async_context context) const { EscapableHandleScope scope(_env); MaybeOrValue result = Value().MakeCallback(recv, args, context); #ifdef NODE_ADDON_API_ENABLE_MAYBE if (result.IsJust()) { return Just(scope.Escape(result.Unwrap())); } return result; #else if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); #endif } inline MaybeOrValue FunctionReference::MakeCallback( napi_value recv, const std::vector& args, napi_async_context context) const { EscapableHandleScope scope(_env); MaybeOrValue result = Value().MakeCallback(recv, args, context); #ifdef NODE_ADDON_API_ENABLE_MAYBE if (result.IsJust()) { return Just(scope.Escape(result.Unwrap())); } return result; #else if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); #endif } inline MaybeOrValue FunctionReference::MakeCallback( napi_value recv, size_t argc, const napi_value* args, napi_async_context context) const { EscapableHandleScope scope(_env); MaybeOrValue result = Value().MakeCallback(recv, argc, args, context); #ifdef NODE_ADDON_API_ENABLE_MAYBE if (result.IsJust()) { return Just(scope.Escape(result.Unwrap())); } return result; #else if (scope.Env().IsExceptionPending()) { return Value(); } return scope.Escape(result); #endif } inline MaybeOrValue FunctionReference::New( const std::initializer_list& args) const { EscapableHandleScope scope(_env); MaybeOrValue result = Value().New(args); #ifdef NODE_ADDON_API_ENABLE_MAYBE if (result.IsJust()) { return Just(scope.Escape(result.Unwrap()).As()); } return result; #else if (scope.Env().IsExceptionPending()) { return Object(); } return scope.Escape(result).As(); #endif } inline MaybeOrValue FunctionReference::New( const std::vector& args) const { EscapableHandleScope scope(_env); MaybeOrValue result = Value().New(args); #ifdef NODE_ADDON_API_ENABLE_MAYBE if (result.IsJust()) { return Just(scope.Escape(result.Unwrap()).As()); } return result; #else if (scope.Env().IsExceptionPending()) { return Object(); } return scope.Escape(result).As(); #endif } //////////////////////////////////////////////////////////////////////////////// // CallbackInfo class //////////////////////////////////////////////////////////////////////////////// inline CallbackInfo::CallbackInfo(napi_env env, napi_callback_info info) : _env(env), _info(info), _this(nullptr), _dynamicArgs(nullptr), _data(nullptr) { _argc = _staticArgCount; _argv = _staticArgs; napi_status status = napi_get_cb_info(env, info, &_argc, _argv, &_this, &_data); NAPI_THROW_IF_FAILED_VOID(_env, status); if (_argc > _staticArgCount) { // Use either a fixed-size array (on the stack) or a dynamically-allocated // array (on the heap) depending on the number of args. _dynamicArgs = new napi_value[_argc]; _argv = _dynamicArgs; status = napi_get_cb_info(env, info, &_argc, _argv, nullptr, nullptr); NAPI_THROW_IF_FAILED_VOID(_env, status); } } inline CallbackInfo::~CallbackInfo() { if (_dynamicArgs != nullptr) { delete[] _dynamicArgs; } } inline CallbackInfo::operator napi_callback_info() const { return _info; } inline Value CallbackInfo::NewTarget() const { napi_value newTarget; napi_status status = napi_get_new_target(_env, _info, &newTarget); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, newTarget); } inline bool CallbackInfo::IsConstructCall() const { return !NewTarget().IsEmpty(); } inline Napi::Env CallbackInfo::Env() const { return Napi::Env(_env); } inline size_t CallbackInfo::Length() const { return _argc; } inline const Value CallbackInfo::operator[](size_t index) const { return index < _argc ? Value(_env, _argv[index]) : Env().Undefined(); } inline Value CallbackInfo::This() const { if (_this == nullptr) { return Env().Undefined(); } return Object(_env, _this); } inline void* CallbackInfo::Data() const { return _data; } inline void CallbackInfo::SetData(void* data) { _data = data; } //////////////////////////////////////////////////////////////////////////////// // PropertyDescriptor class //////////////////////////////////////////////////////////////////////////////// template PropertyDescriptor PropertyDescriptor::Accessor( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = details::TemplatedCallback; desc.attributes = attributes; desc.data = data; return desc; } template PropertyDescriptor PropertyDescriptor::Accessor( const std::string& utf8name, napi_property_attributes attributes, void* data) { return Accessor(utf8name.c_str(), attributes, data); } template PropertyDescriptor PropertyDescriptor::Accessor( Name name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = details::TemplatedCallback; desc.attributes = attributes; desc.data = data; return desc; } template PropertyDescriptor PropertyDescriptor::Accessor( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = details::TemplatedCallback; desc.setter = details::TemplatedVoidCallback; desc.attributes = attributes; desc.data = data; return desc; } template PropertyDescriptor PropertyDescriptor::Accessor( const std::string& utf8name, napi_property_attributes attributes, void* data) { return Accessor(utf8name.c_str(), attributes, data); } template PropertyDescriptor PropertyDescriptor::Accessor( Name name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = details::TemplatedCallback; desc.setter = details::TemplatedVoidCallback; desc.attributes = attributes; desc.data = data; return desc; } template inline PropertyDescriptor PropertyDescriptor::Accessor( Napi::Env env, Napi::Object object, const char* utf8name, Getter getter, napi_property_attributes attributes, void* data) { using CbData = details::CallbackData; auto callbackData = new CbData({getter, data}); napi_status status = AttachData(env, object, callbackData); if (status != napi_ok) { delete callbackData; NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor()); } return PropertyDescriptor({utf8name, nullptr, nullptr, CbData::Wrapper, nullptr, nullptr, attributes, callbackData}); } template inline PropertyDescriptor PropertyDescriptor::Accessor( Napi::Env env, Napi::Object object, const std::string& utf8name, Getter getter, napi_property_attributes attributes, void* data) { return Accessor(env, object, utf8name.c_str(), getter, attributes, data); } template inline PropertyDescriptor PropertyDescriptor::Accessor( Napi::Env env, Napi::Object object, Name name, Getter getter, napi_property_attributes attributes, void* data) { using CbData = details::CallbackData; auto callbackData = new CbData({getter, data}); napi_status status = AttachData(env, object, callbackData); if (status != napi_ok) { delete callbackData; NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor()); } return PropertyDescriptor({nullptr, name, nullptr, CbData::Wrapper, nullptr, nullptr, attributes, callbackData}); } template inline PropertyDescriptor PropertyDescriptor::Accessor( Napi::Env env, Napi::Object object, const char* utf8name, Getter getter, Setter setter, napi_property_attributes attributes, void* data) { using CbData = details::AccessorCallbackData; auto callbackData = new CbData({getter, setter, data}); napi_status status = AttachData(env, object, callbackData); if (status != napi_ok) { delete callbackData; NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor()); } return PropertyDescriptor({utf8name, nullptr, nullptr, CbData::GetterWrapper, CbData::SetterWrapper, nullptr, attributes, callbackData}); } template inline PropertyDescriptor PropertyDescriptor::Accessor( Napi::Env env, Napi::Object object, const std::string& utf8name, Getter getter, Setter setter, napi_property_attributes attributes, void* data) { return Accessor( env, object, utf8name.c_str(), getter, setter, attributes, data); } template inline PropertyDescriptor PropertyDescriptor::Accessor( Napi::Env env, Napi::Object object, Name name, Getter getter, Setter setter, napi_property_attributes attributes, void* data) { using CbData = details::AccessorCallbackData; auto callbackData = new CbData({getter, setter, data}); napi_status status = AttachData(env, object, callbackData); if (status != napi_ok) { delete callbackData; NAPI_THROW_IF_FAILED(env, status, napi_property_descriptor()); } return PropertyDescriptor({nullptr, name, nullptr, CbData::GetterWrapper, CbData::SetterWrapper, nullptr, attributes, callbackData}); } template inline PropertyDescriptor PropertyDescriptor::Function( Napi::Env env, Napi::Object /*object*/, const char* utf8name, Callable cb, napi_property_attributes attributes, void* data) { return PropertyDescriptor({utf8name, nullptr, nullptr, nullptr, nullptr, Napi::Function::New(env, cb, utf8name, data), attributes, nullptr}); } template inline PropertyDescriptor PropertyDescriptor::Function( Napi::Env env, Napi::Object object, const std::string& utf8name, Callable cb, napi_property_attributes attributes, void* data) { return Function(env, object, utf8name.c_str(), cb, attributes, data); } template inline PropertyDescriptor PropertyDescriptor::Function( Napi::Env env, Napi::Object /*object*/, Name name, Callable cb, napi_property_attributes attributes, void* data) { return PropertyDescriptor({nullptr, name, nullptr, nullptr, nullptr, Napi::Function::New(env, cb, nullptr, data), attributes, nullptr}); } inline PropertyDescriptor PropertyDescriptor::Value( const char* utf8name, napi_value value, napi_property_attributes attributes) { return PropertyDescriptor({utf8name, nullptr, nullptr, nullptr, nullptr, value, attributes, nullptr}); } inline PropertyDescriptor PropertyDescriptor::Value( const std::string& utf8name, napi_value value, napi_property_attributes attributes) { return Value(utf8name.c_str(), value, attributes); } inline PropertyDescriptor PropertyDescriptor::Value( napi_value name, napi_value value, napi_property_attributes attributes) { return PropertyDescriptor( {nullptr, name, nullptr, nullptr, nullptr, value, attributes, nullptr}); } inline PropertyDescriptor PropertyDescriptor::Value( Name name, Napi::Value value, napi_property_attributes attributes) { napi_value nameValue = name; napi_value valueValue = value; return PropertyDescriptor::Value(nameValue, valueValue, attributes); } inline PropertyDescriptor::PropertyDescriptor(napi_property_descriptor desc) : _desc(desc) {} inline PropertyDescriptor::operator napi_property_descriptor&() { return _desc; } inline PropertyDescriptor::operator const napi_property_descriptor&() const { return _desc; } //////////////////////////////////////////////////////////////////////////////// // InstanceWrap class //////////////////////////////////////////////////////////////////////////////// template inline void InstanceWrap::AttachPropData( napi_env env, napi_value value, const napi_property_descriptor* prop) { napi_status status; if (!(prop->attributes & napi_static)) { if (prop->method == T::InstanceVoidMethodCallbackWrapper) { status = Napi::details::AttachData( env, value, static_cast(prop->data)); NAPI_THROW_IF_FAILED_VOID(env, status); } else if (prop->method == T::InstanceMethodCallbackWrapper) { status = Napi::details::AttachData( env, value, static_cast(prop->data)); NAPI_THROW_IF_FAILED_VOID(env, status); } else if (prop->getter == T::InstanceGetterCallbackWrapper || prop->setter == T::InstanceSetterCallbackWrapper) { status = Napi::details::AttachData( env, value, static_cast(prop->data)); NAPI_THROW_IF_FAILED_VOID(env, status); } } } template inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( const char* utf8name, InstanceVoidMethodCallback method, napi_property_attributes attributes, void* data) { InstanceVoidMethodCallbackData* callbackData = new InstanceVoidMethodCallbackData({method, data}); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = T::InstanceVoidMethodCallbackWrapper; desc.data = callbackData; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( const char* utf8name, InstanceMethodCallback method, napi_property_attributes attributes, void* data) { InstanceMethodCallbackData* callbackData = new InstanceMethodCallbackData({method, data}); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = T::InstanceMethodCallbackWrapper; desc.data = callbackData; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( Symbol name, InstanceVoidMethodCallback method, napi_property_attributes attributes, void* data) { InstanceVoidMethodCallbackData* callbackData = new InstanceVoidMethodCallbackData({method, data}); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = T::InstanceVoidMethodCallbackWrapper; desc.data = callbackData; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( Symbol name, InstanceMethodCallback method, napi_property_attributes attributes, void* data) { InstanceMethodCallbackData* callbackData = new InstanceMethodCallbackData({method, data}); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = T::InstanceMethodCallbackWrapper; desc.data = callbackData; desc.attributes = attributes; return desc; } template template ::InstanceVoidMethodCallback method> inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = details::TemplatedInstanceVoidCallback; desc.data = data; desc.attributes = attributes; return desc; } template template ::InstanceMethodCallback method> inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = details::TemplatedInstanceCallback; desc.data = data; desc.attributes = attributes; return desc; } template template ::InstanceVoidMethodCallback method> inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = details::TemplatedInstanceVoidCallback; desc.data = data; desc.attributes = attributes; return desc; } template template ::InstanceMethodCallback method> inline ClassPropertyDescriptor InstanceWrap::InstanceMethod( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = details::TemplatedInstanceCallback; desc.data = data; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceAccessor( const char* utf8name, InstanceGetterCallback getter, InstanceSetterCallback setter, napi_property_attributes attributes, void* data) { InstanceAccessorCallbackData* callbackData = new InstanceAccessorCallbackData({getter, setter, data}); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = getter != nullptr ? T::InstanceGetterCallbackWrapper : nullptr; desc.setter = setter != nullptr ? T::InstanceSetterCallbackWrapper : nullptr; desc.data = callbackData; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceAccessor( Symbol name, InstanceGetterCallback getter, InstanceSetterCallback setter, napi_property_attributes attributes, void* data) { InstanceAccessorCallbackData* callbackData = new InstanceAccessorCallbackData({getter, setter, data}); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = getter != nullptr ? T::InstanceGetterCallbackWrapper : nullptr; desc.setter = setter != nullptr ? T::InstanceSetterCallbackWrapper : nullptr; desc.data = callbackData; desc.attributes = attributes; return desc; } template template ::InstanceGetterCallback getter, typename InstanceWrap::InstanceSetterCallback setter> inline ClassPropertyDescriptor InstanceWrap::InstanceAccessor( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = details::TemplatedInstanceCallback; desc.setter = This::WrapSetter(This::SetterTag()); desc.data = data; desc.attributes = attributes; return desc; } template template ::InstanceGetterCallback getter, typename InstanceWrap::InstanceSetterCallback setter> inline ClassPropertyDescriptor InstanceWrap::InstanceAccessor( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = details::TemplatedInstanceCallback; desc.setter = This::WrapSetter(This::SetterTag()); desc.data = data; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceValue( const char* utf8name, Napi::Value value, napi_property_attributes attributes) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.value = value; desc.attributes = attributes; return desc; } template inline ClassPropertyDescriptor InstanceWrap::InstanceValue( Symbol name, Napi::Value value, napi_property_attributes attributes) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.value = value; desc.attributes = attributes; return desc; } template inline napi_value InstanceWrap::InstanceVoidMethodCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback(env, [&] { CallbackInfo callbackInfo(env, info); InstanceVoidMethodCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); T* instance = T::Unwrap(callbackInfo.This().As()); auto cb = callbackData->callback; if (instance) (instance->*cb)(callbackInfo); return nullptr; }); } template inline napi_value InstanceWrap::InstanceMethodCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback(env, [&] { CallbackInfo callbackInfo(env, info); InstanceMethodCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); T* instance = T::Unwrap(callbackInfo.This().As()); auto cb = callbackData->callback; return instance ? (instance->*cb)(callbackInfo) : Napi::Value(); }); } template inline napi_value InstanceWrap::InstanceGetterCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback(env, [&] { CallbackInfo callbackInfo(env, info); InstanceAccessorCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); T* instance = T::Unwrap(callbackInfo.This().As()); auto cb = callbackData->getterCallback; return instance ? (instance->*cb)(callbackInfo) : Napi::Value(); }); } template inline napi_value InstanceWrap::InstanceSetterCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback(env, [&] { CallbackInfo callbackInfo(env, info); InstanceAccessorCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); T* instance = T::Unwrap(callbackInfo.This().As()); auto cb = callbackData->setterCallback; if (instance) (instance->*cb)(callbackInfo, callbackInfo[0]); return nullptr; }); } template template ::InstanceSetterCallback method> inline napi_value InstanceWrap::WrappedMethod( napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback(env, [&] { const CallbackInfo cbInfo(env, info); T* instance = T::Unwrap(cbInfo.This().As()); if (instance) (instance->*method)(cbInfo, cbInfo[0]); return nullptr; }); } //////////////////////////////////////////////////////////////////////////////// // ObjectWrap class //////////////////////////////////////////////////////////////////////////////// template inline ObjectWrap::ObjectWrap(const Napi::CallbackInfo& callbackInfo) { napi_env env = callbackInfo.Env(); napi_value wrapper = callbackInfo.This(); napi_status status; napi_ref ref; T* instance = static_cast(this); status = napi_wrap(env, wrapper, instance, FinalizeCallback, nullptr, &ref); NAPI_THROW_IF_FAILED_VOID(env, status); Reference* instanceRef = instance; *instanceRef = Reference(env, ref); } template inline ObjectWrap::~ObjectWrap() { // If the JS object still exists at this point, remove the finalizer added // through `napi_wrap()`. if (!IsEmpty() && !_finalized) { Object object = Value(); // It is not valid to call `napi_remove_wrap()` with an empty `object`. // This happens e.g. during garbage collection. if (!object.IsEmpty() && _construction_failed) { napi_remove_wrap(Env(), object, nullptr); } } } template inline T* ObjectWrap::Unwrap(Object wrapper) { void* unwrapped; napi_status status = napi_unwrap(wrapper.Env(), wrapper, &unwrapped); NAPI_THROW_IF_FAILED(wrapper.Env(), status, nullptr); return static_cast(unwrapped); } template inline Function ObjectWrap::DefineClass( Napi::Env env, const char* utf8name, const size_t props_count, const napi_property_descriptor* descriptors, void* data) { napi_status status; std::vector props(props_count); // We copy the descriptors to a local array because before defining the class // we must replace static method property descriptors with value property // descriptors such that the value is a function-valued `napi_value` created // with `CreateFunction()`. // // This replacement could be made for instance methods as well, but V8 aborts // if we do that, because it expects methods defined on the prototype template // to have `FunctionTemplate`s. for (size_t index = 0; index < props_count; index++) { props[index] = descriptors[index]; napi_property_descriptor* prop = &props[index]; if (prop->method == T::StaticMethodCallbackWrapper) { status = CreateFunction(env, utf8name, prop->method, static_cast(prop->data), &(prop->value)); NAPI_THROW_IF_FAILED(env, status, Function()); prop->method = nullptr; prop->data = nullptr; } else if (prop->method == T::StaticVoidMethodCallbackWrapper) { status = CreateFunction(env, utf8name, prop->method, static_cast(prop->data), &(prop->value)); NAPI_THROW_IF_FAILED(env, status, Function()); prop->method = nullptr; prop->data = nullptr; } } napi_value value; status = napi_define_class(env, utf8name, NAPI_AUTO_LENGTH, T::ConstructorCallbackWrapper, data, props_count, props.data(), &value); NAPI_THROW_IF_FAILED(env, status, Function()); // After defining the class we iterate once more over the property descriptors // and attach the data associated with accessors and instance methods to the // newly created JavaScript class. for (size_t idx = 0; idx < props_count; idx++) { const napi_property_descriptor* prop = &props[idx]; if (prop->getter == T::StaticGetterCallbackWrapper || prop->setter == T::StaticSetterCallbackWrapper) { status = Napi::details::AttachData( env, value, static_cast(prop->data)); NAPI_THROW_IF_FAILED(env, status, Function()); } else { // InstanceWrap::AttachPropData is responsible for attaching the data // of instance methods and accessors. T::AttachPropData(env, value, prop); } } return Function(env, value); } template inline Function ObjectWrap::DefineClass( Napi::Env env, const char* utf8name, const std::initializer_list>& properties, void* data) { return DefineClass( env, utf8name, properties.size(), reinterpret_cast(properties.begin()), data); } template inline Function ObjectWrap::DefineClass( Napi::Env env, const char* utf8name, const std::vector>& properties, void* data) { return DefineClass( env, utf8name, properties.size(), reinterpret_cast(properties.data()), data); } template inline ClassPropertyDescriptor ObjectWrap::StaticMethod( const char* utf8name, StaticVoidMethodCallback method, napi_property_attributes attributes, void* data) { StaticVoidMethodCallbackData* callbackData = new StaticVoidMethodCallbackData({method, data}); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = T::StaticVoidMethodCallbackWrapper; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticMethod( const char* utf8name, StaticMethodCallback method, napi_property_attributes attributes, void* data) { StaticMethodCallbackData* callbackData = new StaticMethodCallbackData({method, data}); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = T::StaticMethodCallbackWrapper; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticMethod( Symbol name, StaticVoidMethodCallback method, napi_property_attributes attributes, void* data) { StaticVoidMethodCallbackData* callbackData = new StaticVoidMethodCallbackData({method, data}); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = T::StaticVoidMethodCallbackWrapper; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticMethod( Symbol name, StaticMethodCallback method, napi_property_attributes attributes, void* data) { StaticMethodCallbackData* callbackData = new StaticMethodCallbackData({method, data}); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = T::StaticMethodCallbackWrapper; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticVoidMethodCallback method> inline ClassPropertyDescriptor ObjectWrap::StaticMethod( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = details::TemplatedVoidCallback; desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticVoidMethodCallback method> inline ClassPropertyDescriptor ObjectWrap::StaticMethod( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = details::TemplatedVoidCallback; desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticMethodCallback method> inline ClassPropertyDescriptor ObjectWrap::StaticMethod( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = details::TemplatedCallback; desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticMethodCallback method> inline ClassPropertyDescriptor ObjectWrap::StaticMethod( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = details::TemplatedCallback; desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( const char* utf8name, StaticGetterCallback getter, StaticSetterCallback setter, napi_property_attributes attributes, void* data) { StaticAccessorCallbackData* callbackData = new StaticAccessorCallbackData({getter, setter, data}); napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = getter != nullptr ? T::StaticGetterCallbackWrapper : nullptr; desc.setter = setter != nullptr ? T::StaticSetterCallbackWrapper : nullptr; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( Symbol name, StaticGetterCallback getter, StaticSetterCallback setter, napi_property_attributes attributes, void* data) { StaticAccessorCallbackData* callbackData = new StaticAccessorCallbackData({getter, setter, data}); napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = getter != nullptr ? T::StaticGetterCallbackWrapper : nullptr; desc.setter = setter != nullptr ? T::StaticSetterCallbackWrapper : nullptr; desc.data = callbackData; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticGetterCallback getter, typename ObjectWrap::StaticSetterCallback setter> inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( const char* utf8name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = details::TemplatedCallback; desc.setter = This::WrapStaticSetter(This::StaticSetterTag()); desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template template ::StaticGetterCallback getter, typename ObjectWrap::StaticSetterCallback setter> inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( Symbol name, napi_property_attributes attributes, void* data) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = details::TemplatedCallback; desc.setter = This::WrapStaticSetter(This::StaticSetterTag()); desc.data = data; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticValue( const char* utf8name, Napi::Value value, napi_property_attributes attributes) { napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.value = value; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline ClassPropertyDescriptor ObjectWrap::StaticValue( Symbol name, Napi::Value value, napi_property_attributes attributes) { napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.value = value; desc.attributes = static_cast(attributes | napi_static); return desc; } template inline Value ObjectWrap::OnCalledAsFunction( const Napi::CallbackInfo& callbackInfo) { NAPI_THROW( TypeError::New(callbackInfo.Env(), "Class constructors cannot be invoked without 'new'"), Napi::Value()); } template inline void ObjectWrap::Finalize(Napi::Env /*env*/) {} template inline void ObjectWrap::Finalize(BasicEnv /*env*/) {} template inline napi_value ObjectWrap::ConstructorCallbackWrapper( napi_env env, napi_callback_info info) { napi_value new_target; napi_status status = napi_get_new_target(env, info, &new_target); if (status != napi_ok) return nullptr; bool isConstructCall = (new_target != nullptr); if (!isConstructCall) { return details::WrapCallback( env, [&] { return T::OnCalledAsFunction(CallbackInfo(env, info)); }); } napi_value wrapper = details::WrapCallback(env, [&] { CallbackInfo callbackInfo(env, info); T* instance = new T(callbackInfo); #ifdef NODE_ADDON_API_CPP_EXCEPTIONS instance->_construction_failed = false; #else if (callbackInfo.Env().IsExceptionPending()) { // We need to clear the exception so that removing the wrap might work. Error e = callbackInfo.Env().GetAndClearPendingException(); delete instance; e.ThrowAsJavaScriptException(); } else { instance->_construction_failed = false; } #endif // NODE_ADDON_API_CPP_EXCEPTIONS return callbackInfo.This(); }); return wrapper; } template inline napi_value ObjectWrap::StaticVoidMethodCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback(env, [&] { CallbackInfo callbackInfo(env, info); StaticVoidMethodCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); callbackData->callback(callbackInfo); return nullptr; }); } template inline napi_value ObjectWrap::StaticMethodCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback(env, [&] { CallbackInfo callbackInfo(env, info); StaticMethodCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); return callbackData->callback(callbackInfo); }); } template inline napi_value ObjectWrap::StaticGetterCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback(env, [&] { CallbackInfo callbackInfo(env, info); StaticAccessorCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); return callbackData->getterCallback(callbackInfo); }); } template inline napi_value ObjectWrap::StaticSetterCallbackWrapper( napi_env env, napi_callback_info info) { return details::WrapCallback(env, [&] { CallbackInfo callbackInfo(env, info); StaticAccessorCallbackData* callbackData = reinterpret_cast(callbackInfo.Data()); callbackInfo.SetData(callbackData->data); callbackData->setterCallback(callbackInfo, callbackInfo[0]); return nullptr; }); } template inline void ObjectWrap::FinalizeCallback(node_addon_api_basic_env env, void* data, void* /*hint*/) { // If the child class does not override _any_ Finalize() method, `env` will be // unused because of the constexpr guards. Explicitly reference it here to // bypass compiler warnings. (void)env; T* instance = static_cast(data); // Prevent ~ObjectWrap from calling napi_remove_wrap. // The instance->_ref should be deleted with napi_delete_reference in // ~Reference. instance->_finalized = true; // If class overrides the basic finalizer, execute it. if constexpr (details::HasBasicFinalizer::value) { #ifndef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER HandleScope scope(env); #endif instance->Finalize(Napi::BasicEnv(env)); } // If class overrides the (extended) finalizer, either schedule it or // execute it immediately (depending on experimental features enabled). if constexpr (details::HasExtendedFinalizer::value) { #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER // In experimental, attach via node_api_post_finalizer. // `PostFinalizeCallback` is responsible for deleting the `T* instance`, // after calling the user-provided finalizer. napi_status status = node_api_post_finalizer(env, PostFinalizeCallback, data, nullptr); NAPI_FATAL_IF_FAILED(status, "ObjectWrap::FinalizeCallback", "node_api_post_finalizer failed"); #else // In non-experimental, this `FinalizeCallback` already executes from a // non-basic environment. Execute the override directly. // `PostFinalizeCallback` is responsible for deleting the `T* instance`, // after calling the user-provided finalizer. HandleScope scope(env); PostFinalizeCallback(env, data, static_cast(nullptr)); #endif } // If the instance does _not_ override the (extended) finalizer, delete the // `T* instance` immediately. else { delete instance; } } template inline void ObjectWrap::PostFinalizeCallback(napi_env env, void* data, void* /*hint*/) { T* instance = static_cast(data); instance->Finalize(Napi::Env(env)); delete instance; } template template ::StaticSetterCallback method> inline napi_value ObjectWrap::WrappedMethod( napi_env env, napi_callback_info info) NAPI_NOEXCEPT { return details::WrapCallback(env, [&] { const CallbackInfo cbInfo(env, info); // MSVC requires to copy 'method' function pointer to a local variable // before invoking it. auto m = method; m(cbInfo, cbInfo[0]); return nullptr; }); } //////////////////////////////////////////////////////////////////////////////// // HandleScope class //////////////////////////////////////////////////////////////////////////////// inline HandleScope::HandleScope(napi_env env, napi_handle_scope scope) : _env(env), _scope(scope) {} inline HandleScope::HandleScope(Napi::Env env) : _env(env) { napi_status status = napi_open_handle_scope(_env, &_scope); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline HandleScope::~HandleScope() { napi_status status = napi_close_handle_scope(_env, _scope); NAPI_FATAL_IF_FAILED( status, "HandleScope::~HandleScope", "napi_close_handle_scope"); } inline HandleScope::operator napi_handle_scope() const { return _scope; } inline Napi::Env HandleScope::Env() const { return Napi::Env(_env); } //////////////////////////////////////////////////////////////////////////////// // EscapableHandleScope class //////////////////////////////////////////////////////////////////////////////// inline EscapableHandleScope::EscapableHandleScope( napi_env env, napi_escapable_handle_scope scope) : _env(env), _scope(scope) {} inline EscapableHandleScope::EscapableHandleScope(Napi::Env env) : _env(env) { napi_status status = napi_open_escapable_handle_scope(_env, &_scope); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline EscapableHandleScope::~EscapableHandleScope() { napi_status status = napi_close_escapable_handle_scope(_env, _scope); NAPI_FATAL_IF_FAILED(status, "EscapableHandleScope::~EscapableHandleScope", "napi_close_escapable_handle_scope"); } inline EscapableHandleScope::operator napi_escapable_handle_scope() const { return _scope; } inline Napi::Env EscapableHandleScope::Env() const { return Napi::Env(_env); } inline Value EscapableHandleScope::Escape(napi_value escapee) { napi_value result; napi_status status = napi_escape_handle(_env, _scope, escapee, &result); NAPI_THROW_IF_FAILED(_env, status, Value()); return Value(_env, result); } #if (NAPI_VERSION > 2) //////////////////////////////////////////////////////////////////////////////// // CallbackScope class //////////////////////////////////////////////////////////////////////////////// inline CallbackScope::CallbackScope(napi_env env, napi_callback_scope scope) : _env(env), _scope(scope) {} inline CallbackScope::CallbackScope(napi_env env, napi_async_context context) : _env(env) { napi_status status = napi_open_callback_scope(_env, Object::New(env), context, &_scope); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline CallbackScope::~CallbackScope() { napi_status status = napi_close_callback_scope(_env, _scope); NAPI_FATAL_IF_FAILED( status, "CallbackScope::~CallbackScope", "napi_close_callback_scope"); } inline CallbackScope::operator napi_callback_scope() const { return _scope; } inline Napi::Env CallbackScope::Env() const { return Napi::Env(_env); } #endif //////////////////////////////////////////////////////////////////////////////// // AsyncContext class //////////////////////////////////////////////////////////////////////////////// inline AsyncContext::AsyncContext(napi_env env, const char* resource_name) : AsyncContext(env, resource_name, Object::New(env)) {} inline AsyncContext::AsyncContext(napi_env env, const char* resource_name, const Object& resource) : _env(env), _context(nullptr) { napi_value resource_id; napi_status status = napi_create_string_utf8( _env, resource_name, NAPI_AUTO_LENGTH, &resource_id); NAPI_THROW_IF_FAILED_VOID(_env, status); status = napi_async_init(_env, resource, resource_id, &_context); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline AsyncContext::~AsyncContext() { if (_context != nullptr) { napi_async_destroy(_env, _context); _context = nullptr; } } inline AsyncContext::AsyncContext(AsyncContext&& other) { _env = other._env; other._env = nullptr; _context = other._context; other._context = nullptr; } inline AsyncContext& AsyncContext::operator=(AsyncContext&& other) { _env = other._env; other._env = nullptr; _context = other._context; other._context = nullptr; return *this; } inline AsyncContext::operator napi_async_context() const { return _context; } inline Napi::Env AsyncContext::Env() const { return Napi::Env(_env); } //////////////////////////////////////////////////////////////////////////////// // AsyncWorker class //////////////////////////////////////////////////////////////////////////////// #if NAPI_HAS_THREADS inline AsyncWorker::AsyncWorker(const Function& callback) : AsyncWorker(callback, "generic") {} inline AsyncWorker::AsyncWorker(const Function& callback, const char* resource_name) : AsyncWorker(callback, resource_name, Object::New(callback.Env())) {} inline AsyncWorker::AsyncWorker(const Function& callback, const char* resource_name, const Object& resource) : AsyncWorker( Object::New(callback.Env()), callback, resource_name, resource) {} inline AsyncWorker::AsyncWorker(const Object& receiver, const Function& callback) : AsyncWorker(receiver, callback, "generic") {} inline AsyncWorker::AsyncWorker(const Object& receiver, const Function& callback, const char* resource_name) : AsyncWorker( receiver, callback, resource_name, Object::New(callback.Env())) {} inline AsyncWorker::AsyncWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource) : _env(callback.Env()), _receiver(Napi::Persistent(receiver)), _callback(Napi::Persistent(callback)), _suppress_destruct(false) { napi_value resource_id; napi_status status = napi_create_string_latin1( _env, resource_name, NAPI_AUTO_LENGTH, &resource_id); NAPI_THROW_IF_FAILED_VOID(_env, status); status = napi_create_async_work(_env, resource, resource_id, OnAsyncWorkExecute, OnAsyncWorkComplete, this, &_work); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline AsyncWorker::AsyncWorker(Napi::Env env) : AsyncWorker(env, "generic") {} inline AsyncWorker::AsyncWorker(Napi::Env env, const char* resource_name) : AsyncWorker(env, resource_name, Object::New(env)) {} inline AsyncWorker::AsyncWorker(Napi::Env env, const char* resource_name, const Object& resource) : _env(env), _receiver(), _callback(), _suppress_destruct(false) { napi_value resource_id; napi_status status = napi_create_string_latin1( _env, resource_name, NAPI_AUTO_LENGTH, &resource_id); NAPI_THROW_IF_FAILED_VOID(_env, status); status = napi_create_async_work(_env, resource, resource_id, OnAsyncWorkExecute, OnAsyncWorkComplete, this, &_work); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline AsyncWorker::~AsyncWorker() { if (_work != nullptr) { napi_delete_async_work(_env, _work); _work = nullptr; } } inline void AsyncWorker::Destroy() { delete this; } inline AsyncWorker::operator napi_async_work() const { return _work; } inline Napi::Env AsyncWorker::Env() const { return Napi::Env(_env); } inline void AsyncWorker::Queue() { napi_status status = napi_queue_async_work(_env, _work); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline void AsyncWorker::Cancel() { napi_status status = napi_cancel_async_work(_env, _work); NAPI_THROW_IF_FAILED_VOID(_env, status); } inline ObjectReference& AsyncWorker::Receiver() { return _receiver; } inline FunctionReference& AsyncWorker::Callback() { return _callback; } inline void AsyncWorker::SuppressDestruct() { _suppress_destruct = true; } inline void AsyncWorker::OnOK() { if (!_callback.IsEmpty()) { _callback.Call(_receiver.Value(), GetResult(_callback.Env())); } } inline void AsyncWorker::OnError(const Error& e) { if (!_callback.IsEmpty()) { _callback.Call(_receiver.Value(), std::initializer_list{e.Value()}); } } inline void AsyncWorker::SetError(const std::string& error) { _error = error; } inline std::vector AsyncWorker::GetResult(Napi::Env /*env*/) { return {}; } // The OnAsyncWorkExecute method receives an napi_env argument. However, do NOT // use it within this method, as it does not run on the JavaScript thread and // must not run any method that would cause JavaScript to run. In practice, // this means that almost any use of napi_env will be incorrect. inline void AsyncWorker::OnAsyncWorkExecute(napi_env env, void* asyncworker) { AsyncWorker* self = static_cast(asyncworker); self->OnExecute(env); } // The OnExecute method receives an napi_env argument. However, do NOT // use it within this method, as it does not run on the JavaScript thread and // must not run any method that would cause JavaScript to run. In practice, // this means that almost any use of napi_env will be incorrect. inline void AsyncWorker::OnExecute(Napi::Env /*DO_NOT_USE*/) { #ifdef NODE_ADDON_API_CPP_EXCEPTIONS try { Execute(); } catch (const std::exception& e) { SetError(e.what()); } #else // NODE_ADDON_API_CPP_EXCEPTIONS Execute(); #endif // NODE_ADDON_API_CPP_EXCEPTIONS } inline void AsyncWorker::OnAsyncWorkComplete(napi_env env, napi_status status, void* asyncworker) { AsyncWorker* self = static_cast(asyncworker); self->OnWorkComplete(env, status); } inline void AsyncWorker::OnWorkComplete(Napi::Env env, napi_status status) { if (status != napi_cancelled) { HandleScope scope(_env); details::WrapCallback(env, [&] { if (_error.size() == 0) { OnOK(); } else { OnError(Error::New(_env, _error)); } return nullptr; }); } if (!_suppress_destruct) { Destroy(); } } #endif // NAPI_HAS_THREADS #if (NAPI_VERSION > 3 && NAPI_HAS_THREADS) //////////////////////////////////////////////////////////////////////////////// // TypedThreadSafeFunction class //////////////////////////////////////////////////////////////////////////////// // Starting with NAPI 5, the JavaScript function `func` parameter of // `napi_create_threadsafe_function` is optional. #if NAPI_VERSION > 4 // static, with Callback [missing] Resource [missing] Finalizer [missing] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { TypedThreadSafeFunction tsfn; napi_status status = napi_create_threadsafe_function(env, nullptr, nullptr, String::From(env, resourceName), maxQueueSize, initialThreadCount, nullptr, nullptr, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with Callback [missing] Resource [passed] Finalizer [missing] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { TypedThreadSafeFunction tsfn; napi_status status = napi_create_threadsafe_function(env, nullptr, resource, String::From(env, resourceName), maxQueueSize, initialThreadCount, nullptr, nullptr, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with Callback [missing] Resource [missing] Finalizer [passed] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { TypedThreadSafeFunction tsfn; auto* finalizeData = new details:: ThreadSafeFinalize( {data, finalizeCallback}); auto fini = details::ThreadSafeFinalize:: FinalizeFinalizeWrapperWithDataAndContext; napi_status status = napi_create_threadsafe_function(env, nullptr, nullptr, String::From(env, resourceName), maxQueueSize, initialThreadCount, finalizeData, fini, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with Callback [missing] Resource [passed] Finalizer [passed] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { TypedThreadSafeFunction tsfn; auto* finalizeData = new details:: ThreadSafeFinalize( {data, finalizeCallback}); auto fini = details::ThreadSafeFinalize:: FinalizeFinalizeWrapperWithDataAndContext; napi_status status = napi_create_threadsafe_function(env, nullptr, resource, String::From(env, resourceName), maxQueueSize, initialThreadCount, finalizeData, fini, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } #endif // static, with Callback [passed] Resource [missing] Finalizer [missing] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { TypedThreadSafeFunction tsfn; napi_status status = napi_create_threadsafe_function(env, callback, nullptr, String::From(env, resourceName), maxQueueSize, initialThreadCount, nullptr, nullptr, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with Callback [passed] Resource [passed] Finalizer [missing] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { TypedThreadSafeFunction tsfn; napi_status status = napi_create_threadsafe_function(env, callback, resource, String::From(env, resourceName), maxQueueSize, initialThreadCount, nullptr, nullptr, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with Callback [passed] Resource [missing] Finalizer [passed] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { TypedThreadSafeFunction tsfn; auto* finalizeData = new details:: ThreadSafeFinalize( {data, finalizeCallback}); auto fini = details::ThreadSafeFinalize:: FinalizeFinalizeWrapperWithDataAndContext; napi_status status = napi_create_threadsafe_function(env, callback, nullptr, String::From(env, resourceName), maxQueueSize, initialThreadCount, finalizeData, fini, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } // static, with: Callback [passed] Resource [passed] Finalizer [passed] template template inline TypedThreadSafeFunction TypedThreadSafeFunction::New( napi_env env, CallbackType callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { TypedThreadSafeFunction tsfn; auto* finalizeData = new details:: ThreadSafeFinalize( {data, finalizeCallback}); auto fini = details::ThreadSafeFinalize:: FinalizeFinalizeWrapperWithDataAndContext; napi_status status = napi_create_threadsafe_function( env, details::DefaultCallbackWrapper< CallbackType, TypedThreadSafeFunction>(env, callback), resource, String::From(env, resourceName), maxQueueSize, initialThreadCount, finalizeData, fini, context, CallJsInternal, &tsfn._tsfn); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED( env, status, TypedThreadSafeFunction()); } return tsfn; } template inline TypedThreadSafeFunction:: TypedThreadSafeFunction() : _tsfn() {} template inline TypedThreadSafeFunction:: TypedThreadSafeFunction(napi_threadsafe_function tsfn) : _tsfn(tsfn) {} template inline TypedThreadSafeFunction:: operator napi_threadsafe_function() const { return _tsfn; } template inline napi_status TypedThreadSafeFunction::BlockingCall( DataType* data) const { return napi_call_threadsafe_function(_tsfn, data, napi_tsfn_blocking); } template inline napi_status TypedThreadSafeFunction::NonBlockingCall( DataType* data) const { return napi_call_threadsafe_function(_tsfn, data, napi_tsfn_nonblocking); } template inline void TypedThreadSafeFunction::Ref( napi_env env) const { if (_tsfn != nullptr) { napi_status status = napi_ref_threadsafe_function(env, _tsfn); NAPI_THROW_IF_FAILED_VOID(env, status); } } template inline void TypedThreadSafeFunction::Unref( napi_env env) const { if (_tsfn != nullptr) { napi_status status = napi_unref_threadsafe_function(env, _tsfn); NAPI_THROW_IF_FAILED_VOID(env, status); } } template inline napi_status TypedThreadSafeFunction::Acquire() const { return napi_acquire_threadsafe_function(_tsfn); } template inline napi_status TypedThreadSafeFunction::Release() const { return napi_release_threadsafe_function(_tsfn, napi_tsfn_release); } template inline napi_status TypedThreadSafeFunction::Abort() const { return napi_release_threadsafe_function(_tsfn, napi_tsfn_abort); } template inline ContextType* TypedThreadSafeFunction::GetContext() const { void* context; napi_status status = napi_get_threadsafe_function_context(_tsfn, &context); NAPI_FATAL_IF_FAILED(status, "TypedThreadSafeFunction::GetContext", "napi_get_threadsafe_function_context"); return static_cast(context); } // static template void TypedThreadSafeFunction::CallJsInternal( napi_env env, napi_value jsCallback, void* context, void* data) { details::CallJsWrapper( env, jsCallback, context, data); } #if NAPI_VERSION == 4 // static template Napi::Function TypedThreadSafeFunction::EmptyFunctionFactory( Napi::Env env) { return Napi::Function::New(env, [](const CallbackInfo& cb) {}); } // static template Napi::Function TypedThreadSafeFunction::FunctionOrEmpty( Napi::Env env, Napi::Function& callback) { if (callback.IsEmpty()) { return EmptyFunctionFactory(env); } return callback; } #else // static template std::nullptr_t TypedThreadSafeFunction::EmptyFunctionFactory( Napi::Env /*env*/) { return nullptr; } // static template Napi::Function TypedThreadSafeFunction::FunctionOrEmpty( Napi::Env /*env*/, Napi::Function& callback) { return callback; } #endif //////////////////////////////////////////////////////////////////////////////// // ThreadSafeFunction class //////////////////////////////////////////////////////////////////////////////// // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount) { return New( env, callback, Object(), resourceName, maxQueueSize, initialThreadCount); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount, context); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount, finalizeCallback); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback, FinalizerDataType* data) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount, finalizeCallback, data); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount, context, finalizeCallback); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { return New(env, callback, Object(), resourceName, maxQueueSize, initialThreadCount, context, finalizeCallback, data); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount) { return New(env, callback, resource, resourceName, maxQueueSize, initialThreadCount, static_cast(nullptr) /* context */); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context) { return New(env, callback, resource, resourceName, maxQueueSize, initialThreadCount, context, [](Env, ContextType*) {} /* empty finalizer */); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback) { return New(env, callback, resource, resourceName, maxQueueSize, initialThreadCount, static_cast(nullptr) /* context */, finalizeCallback, static_cast(nullptr) /* data */, details::ThreadSafeFinalize::Wrapper); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback, FinalizerDataType* data) { return New(env, callback, resource, resourceName, maxQueueSize, initialThreadCount, static_cast(nullptr) /* context */, finalizeCallback, data, details::ThreadSafeFinalize:: FinalizeWrapperWithData); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback) { return New( env, callback, resource, resourceName, maxQueueSize, initialThreadCount, context, finalizeCallback, static_cast(nullptr) /* data */, details::ThreadSafeFinalize::FinalizeWrapperWithContext); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data) { return New( env, callback, resource, resourceName, maxQueueSize, initialThreadCount, context, finalizeCallback, data, details::ThreadSafeFinalize:: FinalizeFinalizeWrapperWithDataAndContext); } inline ThreadSafeFunction::ThreadSafeFunction() : _tsfn() {} inline ThreadSafeFunction::ThreadSafeFunction(napi_threadsafe_function tsfn) : _tsfn(tsfn) {} inline ThreadSafeFunction::operator napi_threadsafe_function() const { return _tsfn; } inline napi_status ThreadSafeFunction::BlockingCall() const { return CallInternal(nullptr, napi_tsfn_blocking); } template <> inline napi_status ThreadSafeFunction::BlockingCall(void* data) const { return napi_call_threadsafe_function(_tsfn, data, napi_tsfn_blocking); } template inline napi_status ThreadSafeFunction::BlockingCall(Callback callback) const { return CallInternal(new CallbackWrapper(callback), napi_tsfn_blocking); } template inline napi_status ThreadSafeFunction::BlockingCall(DataType* data, Callback callback) const { auto wrapper = [data, callback](Env env, Function jsCallback) { callback(env, jsCallback, data); }; return CallInternal(new CallbackWrapper(wrapper), napi_tsfn_blocking); } inline napi_status ThreadSafeFunction::NonBlockingCall() const { return CallInternal(nullptr, napi_tsfn_nonblocking); } template <> inline napi_status ThreadSafeFunction::NonBlockingCall(void* data) const { return napi_call_threadsafe_function(_tsfn, data, napi_tsfn_nonblocking); } template inline napi_status ThreadSafeFunction::NonBlockingCall( Callback callback) const { return CallInternal(new CallbackWrapper(callback), napi_tsfn_nonblocking); } template inline napi_status ThreadSafeFunction::NonBlockingCall( DataType* data, Callback callback) const { auto wrapper = [data, callback](Env env, Function jsCallback) { callback(env, jsCallback, data); }; return CallInternal(new CallbackWrapper(wrapper), napi_tsfn_nonblocking); } inline void ThreadSafeFunction::Ref(napi_env env) const { if (_tsfn != nullptr) { napi_status status = napi_ref_threadsafe_function(env, _tsfn); NAPI_THROW_IF_FAILED_VOID(env, status); } } inline void ThreadSafeFunction::Unref(napi_env env) const { if (_tsfn != nullptr) { napi_status status = napi_unref_threadsafe_function(env, _tsfn); NAPI_THROW_IF_FAILED_VOID(env, status); } } inline napi_status ThreadSafeFunction::Acquire() const { return napi_acquire_threadsafe_function(_tsfn); } inline napi_status ThreadSafeFunction::Release() const { return napi_release_threadsafe_function(_tsfn, napi_tsfn_release); } inline napi_status ThreadSafeFunction::Abort() const { return napi_release_threadsafe_function(_tsfn, napi_tsfn_abort); } inline ThreadSafeFunction::ConvertibleContext ThreadSafeFunction::GetContext() const { void* context; napi_status status = napi_get_threadsafe_function_context(_tsfn, &context); NAPI_FATAL_IF_FAILED(status, "ThreadSafeFunction::GetContext", "napi_get_threadsafe_function_context"); return ConvertibleContext({context}); } // static template inline ThreadSafeFunction ThreadSafeFunction::New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data, napi_finalize wrapper) { static_assert(details::can_make_string::value || std::is_convertible::value, "Resource name should be convertible to the string type"); ThreadSafeFunction tsfn; auto* finalizeData = new details:: ThreadSafeFinalize( {data, finalizeCallback}); napi_status status = napi_create_threadsafe_function(env, callback, resource, Value::From(env, resourceName), maxQueueSize, initialThreadCount, finalizeData, wrapper, context, CallJS, &tsfn._tsfn); if (status != napi_ok) { delete finalizeData; NAPI_THROW_IF_FAILED(env, status, ThreadSafeFunction()); } return tsfn; } inline napi_status ThreadSafeFunction::CallInternal( CallbackWrapper* callbackWrapper, napi_threadsafe_function_call_mode mode) const { napi_status status = napi_call_threadsafe_function(_tsfn, callbackWrapper, mode); if (status != napi_ok && callbackWrapper != nullptr) { delete callbackWrapper; } return status; } // static inline void ThreadSafeFunction::CallJS(napi_env env, napi_value jsCallback, void* /* context */, void* data) { if (env == nullptr && jsCallback == nullptr) { return; } details::WrapVoidCallback(env, [&]() { if (data != nullptr) { auto* callbackWrapper = static_cast(data); (*callbackWrapper)(env, Function(env, jsCallback)); delete callbackWrapper; } else if (jsCallback != nullptr) { Function(env, jsCallback).Call({}); } }); } //////////////////////////////////////////////////////////////////////////////// // Async Progress Worker Base class //////////////////////////////////////////////////////////////////////////////// template inline AsyncProgressWorkerBase::AsyncProgressWorkerBase( const Object& receiver, const Function& callback, const char* resource_name, const Object& resource, size_t queue_size) : AsyncWorker(receiver, callback, resource_name, resource) { // Fill all possible arguments to work around ambiguous // ThreadSafeFunction::New signatures. _tsfn = ThreadSafeFunction::New(callback.Env(), callback, resource, resource_name, queue_size, /** initialThreadCount */ 1, /** context */ this, OnThreadSafeFunctionFinalize, /** finalizeData */ this); } #if NAPI_VERSION > 4 template inline AsyncProgressWorkerBase::AsyncProgressWorkerBase( Napi::Env env, const char* resource_name, const Object& resource, size_t queue_size) : AsyncWorker(env, resource_name, resource) { // TODO: Once the changes to make the callback optional for threadsafe // functions are available on all versions we can remove the dummy Function // here. Function callback; // Fill all possible arguments to work around ambiguous // ThreadSafeFunction::New signatures. _tsfn = ThreadSafeFunction::New(env, callback, resource, resource_name, queue_size, /** initialThreadCount */ 1, /** context */ this, OnThreadSafeFunctionFinalize, /** finalizeData */ this); } #endif template inline AsyncProgressWorkerBase::~AsyncProgressWorkerBase() { // Abort pending tsfn call. // Don't send progress events after we've already completed. // It's ok to call ThreadSafeFunction::Abort and ThreadSafeFunction::Release // duplicated. _tsfn.Abort(); } template inline void AsyncProgressWorkerBase::OnAsyncWorkProgress( Napi::Env /* env */, Napi::Function /* jsCallback */, void* data) { ThreadSafeData* tsd = static_cast(data); tsd->asyncprogressworker()->OnWorkProgress(tsd->data()); delete tsd; } template inline napi_status AsyncProgressWorkerBase::NonBlockingCall( DataType* data) { auto tsd = new AsyncProgressWorkerBase::ThreadSafeData(this, data); auto ret = _tsfn.NonBlockingCall(tsd, OnAsyncWorkProgress); if (ret != napi_ok) { delete tsd; } return ret; } template inline void AsyncProgressWorkerBase::OnWorkComplete( Napi::Env /* env */, napi_status status) { _work_completed = true; _complete_status = status; _tsfn.Release(); } template inline void AsyncProgressWorkerBase::OnThreadSafeFunctionFinalize( Napi::Env env, void* /* data */, AsyncProgressWorkerBase* context) { if (context->_work_completed) { context->AsyncWorker::OnWorkComplete(env, context->_complete_status); } } //////////////////////////////////////////////////////////////////////////////// // Async Progress Worker class //////////////////////////////////////////////////////////////////////////////// template inline AsyncProgressWorker::AsyncProgressWorker(const Function& callback) : AsyncProgressWorker(callback, "generic") {} template inline AsyncProgressWorker::AsyncProgressWorker(const Function& callback, const char* resource_name) : AsyncProgressWorker( callback, resource_name, Object::New(callback.Env())) {} template inline AsyncProgressWorker::AsyncProgressWorker(const Function& callback, const char* resource_name, const Object& resource) : AsyncProgressWorker( Object::New(callback.Env()), callback, resource_name, resource) {} template inline AsyncProgressWorker::AsyncProgressWorker(const Object& receiver, const Function& callback) : AsyncProgressWorker(receiver, callback, "generic") {} template inline AsyncProgressWorker::AsyncProgressWorker(const Object& receiver, const Function& callback, const char* resource_name) : AsyncProgressWorker( receiver, callback, resource_name, Object::New(callback.Env())) {} template inline AsyncProgressWorker::AsyncProgressWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource) : AsyncProgressWorkerBase(receiver, callback, resource_name, resource), _asyncdata(nullptr), _asyncsize(0), _signaled(false) {} #if NAPI_VERSION > 4 template inline AsyncProgressWorker::AsyncProgressWorker(Napi::Env env) : AsyncProgressWorker(env, "generic") {} template inline AsyncProgressWorker::AsyncProgressWorker(Napi::Env env, const char* resource_name) : AsyncProgressWorker(env, resource_name, Object::New(env)) {} template inline AsyncProgressWorker::AsyncProgressWorker(Napi::Env env, const char* resource_name, const Object& resource) : AsyncProgressWorkerBase(env, resource_name, resource), _asyncdata(nullptr), _asyncsize(0) {} #endif template inline AsyncProgressWorker::~AsyncProgressWorker() { { std::lock_guard lock(this->_mutex); _asyncdata = nullptr; _asyncsize = 0; } } template inline void AsyncProgressWorker::Execute() { ExecutionProgress progress(this); Execute(progress); } template inline void AsyncProgressWorker::OnWorkProgress(void*) { T* data; size_t size; bool signaled; { std::lock_guard lock(this->_mutex); data = this->_asyncdata; size = this->_asyncsize; signaled = this->_signaled; this->_asyncdata = nullptr; this->_asyncsize = 0; this->_signaled = false; } /** * The callback of ThreadSafeFunction is not been invoked immediately on the * callback of uv_async_t (uv io poll), rather the callback of TSFN is * invoked on the right next uv idle callback. There are chances that during * the deferring the signal of uv_async_t is been sent again, i.e. potential * not coalesced two calls of the TSFN callback. */ if (data == nullptr && !signaled) { return; } this->OnProgress(data, size); delete[] data; } template inline void AsyncProgressWorker::SendProgress_(const T* data, size_t count) { T* new_data = new T[count]; std::copy(data, data + count, new_data); T* old_data; { std::lock_guard lock(this->_mutex); old_data = _asyncdata; _asyncdata = new_data; _asyncsize = count; _signaled = false; } this->NonBlockingCall(nullptr); delete[] old_data; } template inline void AsyncProgressWorker::Signal() { { std::lock_guard lock(this->_mutex); _signaled = true; } this->NonBlockingCall(static_cast(nullptr)); } template inline void AsyncProgressWorker::ExecutionProgress::Signal() const { this->_worker->Signal(); } template inline void AsyncProgressWorker::ExecutionProgress::Send( const T* data, size_t count) const { _worker->SendProgress_(data, count); } //////////////////////////////////////////////////////////////////////////////// // Async Progress Queue Worker class //////////////////////////////////////////////////////////////////////////////// template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker( const Function& callback) : AsyncProgressQueueWorker(callback, "generic") {} template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker( const Function& callback, const char* resource_name) : AsyncProgressQueueWorker( callback, resource_name, Object::New(callback.Env())) {} template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker( const Function& callback, const char* resource_name, const Object& resource) : AsyncProgressQueueWorker( Object::New(callback.Env()), callback, resource_name, resource) {} template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker( const Object& receiver, const Function& callback) : AsyncProgressQueueWorker(receiver, callback, "generic") {} template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker( const Object& receiver, const Function& callback, const char* resource_name) : AsyncProgressQueueWorker( receiver, callback, resource_name, Object::New(callback.Env())) {} template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker( const Object& receiver, const Function& callback, const char* resource_name, const Object& resource) : AsyncProgressWorkerBase>( receiver, callback, resource_name, resource, /** unlimited queue size */ 0) {} #if NAPI_VERSION > 4 template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker(Napi::Env env) : AsyncProgressQueueWorker(env, "generic") {} template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker( Napi::Env env, const char* resource_name) : AsyncProgressQueueWorker(env, resource_name, Object::New(env)) {} template inline AsyncProgressQueueWorker::AsyncProgressQueueWorker( Napi::Env env, const char* resource_name, const Object& resource) : AsyncProgressWorkerBase>( env, resource_name, resource, /** unlimited queue size */ 0) {} #endif template inline void AsyncProgressQueueWorker::Execute() { ExecutionProgress progress(this); Execute(progress); } template inline void AsyncProgressQueueWorker::OnWorkProgress( std::pair* datapair) { if (datapair == nullptr) { return; } T* data = datapair->first; size_t size = datapair->second; this->OnProgress(data, size); delete datapair; delete[] data; } template inline void AsyncProgressQueueWorker::SendProgress_(const T* data, size_t count) { T* new_data = new T[count]; std::copy(data, data + count, new_data); auto pair = new std::pair(new_data, count); this->NonBlockingCall(pair); } template inline void AsyncProgressQueueWorker::Signal() const { this->SendProgress_(static_cast(nullptr), 0); } template inline void AsyncProgressQueueWorker::OnWorkComplete(Napi::Env env, napi_status status) { // Draining queued items in TSFN. AsyncProgressWorkerBase>::OnWorkComplete(env, status); } template inline void AsyncProgressQueueWorker::ExecutionProgress::Signal() const { _worker->SendProgress_(static_cast(nullptr), 0); } template inline void AsyncProgressQueueWorker::ExecutionProgress::Send( const T* data, size_t count) const { _worker->SendProgress_(data, count); } #endif // NAPI_VERSION > 3 && NAPI_HAS_THREADS //////////////////////////////////////////////////////////////////////////////// // Memory Management class //////////////////////////////////////////////////////////////////////////////// inline int64_t MemoryManagement::AdjustExternalMemory(BasicEnv env, int64_t change_in_bytes) { int64_t result; napi_status status = napi_adjust_external_memory(env, change_in_bytes, &result); NAPI_FATAL_IF_FAILED(status, "MemoryManagement::AdjustExternalMemory", "napi_adjust_external_memory"); return result; } //////////////////////////////////////////////////////////////////////////////// // Version Management class //////////////////////////////////////////////////////////////////////////////// inline uint32_t VersionManagement::GetNapiVersion(BasicEnv env) { uint32_t result; napi_status status = napi_get_version(env, &result); NAPI_FATAL_IF_FAILED( status, "VersionManagement::GetNapiVersion", "napi_get_version"); return result; } inline const napi_node_version* VersionManagement::GetNodeVersion( BasicEnv env) { const napi_node_version* result; napi_status status = napi_get_node_version(env, &result); NAPI_FATAL_IF_FAILED( status, "VersionManagement::GetNodeVersion", "napi_get_node_version"); return result; } #if NAPI_VERSION > 5 //////////////////////////////////////////////////////////////////////////////// // Addon class //////////////////////////////////////////////////////////////////////////////// template inline Object Addon::Init(Env env, Object exports) { T* addon = new T(env, exports); env.SetInstanceData(addon); return addon->entry_point_; } template inline T* Addon::Unwrap(Object wrapper) { return wrapper.Env().GetInstanceData(); } template inline void Addon::DefineAddon( Object exports, const std::initializer_list& props) { DefineProperties(exports, props); entry_point_ = exports; } template inline Napi::Object Addon::DefineProperties( Object object, const std::initializer_list& props) { const napi_property_descriptor* properties = reinterpret_cast(props.begin()); size_t size = props.size(); napi_status status = napi_define_properties(object.Env(), object, size, properties); NAPI_THROW_IF_FAILED(object.Env(), status, object); for (size_t idx = 0; idx < size; idx++) T::AttachPropData(object.Env(), object, &properties[idx]); return object; } #endif // NAPI_VERSION > 5 #if NAPI_VERSION > 2 template Env::CleanupHook BasicEnv::AddCleanupHook(Hook hook, Arg* arg) { return CleanupHook(*this, hook, arg); } template Env::CleanupHook BasicEnv::AddCleanupHook(Hook hook) { return CleanupHook(*this, hook); } template Env::CleanupHook::CleanupHook() { data = nullptr; } template Env::CleanupHook::CleanupHook(Napi::BasicEnv env, Hook hook) : wrapper(Env::CleanupHook::Wrapper) { data = new CleanupData{std::move(hook), nullptr}; napi_status status = napi_add_env_cleanup_hook(env, wrapper, data); if (status != napi_ok) { delete data; data = nullptr; } } template Env::CleanupHook::CleanupHook(Napi::BasicEnv env, Hook hook, Arg* arg) : wrapper(Env::CleanupHook::WrapperWithArg) { data = new CleanupData{std::move(hook), arg}; napi_status status = napi_add_env_cleanup_hook(env, wrapper, data); if (status != napi_ok) { delete data; data = nullptr; } } template bool Env::CleanupHook::Remove(BasicEnv env) { napi_status status = napi_remove_env_cleanup_hook(env, wrapper, data); delete data; data = nullptr; return status == napi_ok; } template bool Env::CleanupHook::IsEmpty() const { return data == nullptr; } #endif // NAPI_VERSION > 2 #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER template inline void BasicEnv::PostFinalizer(FinalizerType finalizeCallback) const { using T = void*; details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), nullptr}); napi_status status = node_api_post_finalizer( _env, details::FinalizeData::WrapperGCWithoutData, static_cast(nullptr), finalizeData); if (status != napi_ok) { delete finalizeData; NAPI_FATAL_IF_FAILED( status, "BasicEnv::PostFinalizer", "invalid arguments"); } } template inline void BasicEnv::PostFinalizer(FinalizerType finalizeCallback, T* data) const { details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), nullptr}); napi_status status = node_api_post_finalizer( _env, details::FinalizeData::WrapperGC, data, finalizeData); if (status != napi_ok) { delete finalizeData; NAPI_FATAL_IF_FAILED( status, "BasicEnv::PostFinalizer", "invalid arguments"); } } template inline void BasicEnv::PostFinalizer(FinalizerType finalizeCallback, T* data, Hint* finalizeHint) const { details::FinalizeData* finalizeData = new details::FinalizeData( {std::move(finalizeCallback), finalizeHint}); napi_status status = node_api_post_finalizer( _env, details::FinalizeData::WrapperGCWithHint, data, finalizeData); if (status != napi_ok) { delete finalizeData; NAPI_FATAL_IF_FAILED( status, "BasicEnv::PostFinalizer", "invalid arguments"); } } #endif // NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER #ifdef NAPI_CPP_CUSTOM_NAMESPACE } // namespace NAPI_CPP_CUSTOM_NAMESPACE #endif } // namespace Napi #endif // SRC_NAPI_INL_H_ #ifndef SRC_NAPI_H_ #define SRC_NAPI_H_ #ifndef NAPI_HAS_THREADS #if !defined(__wasm__) || (defined(__EMSCRIPTEN_PTHREADS__) || \ (defined(__wasi__) && defined(_REENTRANT))) #define NAPI_HAS_THREADS 1 #else #define NAPI_HAS_THREADS 0 #endif #endif #include #include #include #include #if NAPI_HAS_THREADS #include #endif // NAPI_HAS_THREADS #include #include // VS2015 RTM has bugs with constexpr, so require min of VS2015 Update 3 (known // good version) #if !defined(_MSC_VER) || _MSC_FULL_VER >= 190024210 #define NAPI_HAS_CONSTEXPR 1 #endif // VS2013 does not support char16_t literal strings, so we'll work around it // using wchar_t strings and casting them. This is safe as long as the character // sizes are the same. #if defined(_MSC_VER) && _MSC_VER <= 1800 static_assert(sizeof(char16_t) == sizeof(wchar_t), "Size mismatch between char16_t and wchar_t"); #define NAPI_WIDE_TEXT(x) reinterpret_cast(L##x) #else #define NAPI_WIDE_TEXT(x) u##x #endif // Backwards-compatibility to handle the rename of this macro definition, in // case they are used within userland code. #ifdef NAPI_CPP_EXCEPTIONS #define NODE_ADDON_API_CPP_EXCEPTIONS #endif #if defined(NODE_ADDON_API_CPP_EXCEPTIONS) && !defined(NAPI_CPP_EXCEPTIONS) #define NAPI_CPP_EXCEPTIONS #endif #ifdef NAPI_DISABLE_CPP_EXCEPTIONS #define NODE_ADDON_API_DISABLE_CPP_EXCEPTIONS #endif #if defined(NODE_ADDON_API_DISABLE_CPP_EXCEPTIONS) && \ !defined(NAPI_DISABLE_CPP_EXCEPTIONS) #define NAPI_DISABLE_CPP_EXCEPTIONS #endif // If C++ exceptions are not explicitly enabled or disabled, enable them // if exceptions were enabled in the compiler settings. #if !defined(NODE_ADDON_API_CPP_EXCEPTIONS) && \ !defined(NODE_ADDON_API_DISABLE_CPP_EXCEPTIONS) #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) #define NODE_ADDON_API_CPP_EXCEPTIONS #else #error Exception support not detected. \ Define either NODE_ADDON_API_CPP_EXCEPTIONS or NODE_ADDON_API_DISABLE_CPP_EXCEPTIONS. #endif #endif // If C++ NODE_ADDON_API_CPP_EXCEPTIONS are enabled, NODE_ADDON_API_ENABLE_MAYBE // should not be set #if defined(NODE_ADDON_API_CPP_EXCEPTIONS) && \ defined(NODE_ADDON_API_ENABLE_MAYBE) #error NODE_ADDON_API_ENABLE_MAYBE should not be set when \ NODE_ADDON_API_CPP_EXCEPTIONS is defined. #endif #ifdef _NOEXCEPT #define NAPI_NOEXCEPT _NOEXCEPT #else #define NAPI_NOEXCEPT noexcept #endif #ifdef NODE_ADDON_API_CPP_EXCEPTIONS // When C++ exceptions are enabled, Errors are thrown directly. There is no need // to return anything after the throw statements. The variadic parameter is an // optional return value that is ignored. // We need _VOID versions of the macros to avoid warnings resulting from // leaving the NAPI_THROW_* `...` argument empty. #define NAPI_THROW(e, ...) throw e #define NAPI_THROW_VOID(e) throw e #define NAPI_THROW_IF_FAILED(env, status, ...) \ if ((status) != napi_ok) throw Napi::Error::New(env); #define NAPI_THROW_IF_FAILED_VOID(env, status) \ if ((status) != napi_ok) throw Napi::Error::New(env); #else // NODE_ADDON_API_CPP_EXCEPTIONS // When C++ exceptions are disabled, Errors are thrown as JavaScript exceptions, // which are pending until the callback returns to JS. The variadic parameter // is an optional return value; usually it is an empty result. // We need _VOID versions of the macros to avoid warnings resulting from // leaving the NAPI_THROW_* `...` argument empty. #define NAPI_THROW(e, ...) \ do { \ (e).ThrowAsJavaScriptException(); \ return __VA_ARGS__; \ } while (0) #define NAPI_THROW_VOID(e) \ do { \ (e).ThrowAsJavaScriptException(); \ return; \ } while (0) #define NAPI_THROW_IF_FAILED(env, status, ...) \ if ((status) != napi_ok) { \ Napi::Error::New(env).ThrowAsJavaScriptException(); \ return __VA_ARGS__; \ } #define NAPI_THROW_IF_FAILED_VOID(env, status) \ if ((status) != napi_ok) { \ Napi::Error::New(env).ThrowAsJavaScriptException(); \ return; \ } #endif // NODE_ADDON_API_CPP_EXCEPTIONS #ifdef NODE_ADDON_API_ENABLE_MAYBE #define NAPI_MAYBE_THROW_IF_FAILED(env, status, type) \ NAPI_THROW_IF_FAILED(env, status, Napi::Nothing()) #define NAPI_RETURN_OR_THROW_IF_FAILED(env, status, result, type) \ NAPI_MAYBE_THROW_IF_FAILED(env, status, type); \ return Napi::Just(result); #else #define NAPI_MAYBE_THROW_IF_FAILED(env, status, type) \ NAPI_THROW_IF_FAILED(env, status, type()) #define NAPI_RETURN_OR_THROW_IF_FAILED(env, status, result, type) \ NAPI_MAYBE_THROW_IF_FAILED(env, status, type); \ return result; #endif #define NAPI_DISALLOW_ASSIGN(CLASS) void operator=(const CLASS&) = delete; #define NAPI_DISALLOW_COPY(CLASS) CLASS(const CLASS&) = delete; #define NAPI_DISALLOW_ASSIGN_COPY(CLASS) \ NAPI_DISALLOW_ASSIGN(CLASS) \ NAPI_DISALLOW_COPY(CLASS) #define NAPI_CHECK(condition, location, message) \ do { \ if (!(condition)) { \ Napi::Error::Fatal((location), (message)); \ } \ } while (0) // Internal check helper. Be careful that the formatted message length should be // max 255 size and null terminated. #define NAPI_INTERNAL_CHECK(expr, location, ...) \ do { \ if (!(expr)) { \ std::string msg = Napi::details::StringFormat(__VA_ARGS__); \ Napi::Error::Fatal(location, msg.c_str()); \ } \ } while (0) #define NAPI_INTERNAL_CHECK_EQ(actual, expected, value_format, location) \ do { \ auto actual_value = (actual); \ NAPI_INTERNAL_CHECK(actual_value == (expected), \ location, \ "Expected " #actual " to be equal to " #expected \ ", but got " value_format ".", \ actual_value); \ } while (0) #define NAPI_FATAL_IF_FAILED(status, location, message) \ NAPI_CHECK((status) == napi_ok, location, message) //////////////////////////////////////////////////////////////////////////////// /// Node-API C++ Wrapper Classes /// /// These classes wrap the "Node-API" ABI-stable C APIs for Node.js, providing a /// C++ object model and C++ exception-handling semantics with low overhead. /// The wrappers are all header-only so that they do not affect the ABI. //////////////////////////////////////////////////////////////////////////////// namespace Napi { #ifdef NAPI_CPP_CUSTOM_NAMESPACE // NAPI_CPP_CUSTOM_NAMESPACE can be #define'd per-addon to avoid symbol // conflicts between different instances of node-addon-api // First dummy definition of the namespace to make sure that Napi::(name) still // refers to the right things inside this file. namespace NAPI_CPP_CUSTOM_NAMESPACE {} using namespace NAPI_CPP_CUSTOM_NAMESPACE; namespace NAPI_CPP_CUSTOM_NAMESPACE { #endif // Forward declarations class Env; class Value; class Boolean; class Number; #if NAPI_VERSION > 5 class BigInt; #endif // NAPI_VERSION > 5 #if (NAPI_VERSION > 4) class Date; #endif class String; class Object; class Array; class ArrayBuffer; class Function; class Error; class PropertyDescriptor; class CallbackInfo; class TypedArray; template class TypedArrayOf; using Int8Array = TypedArrayOf; ///< Typed-array of signed 8-bit integers using Uint8Array = TypedArrayOf; ///< Typed-array of unsigned 8-bit integers using Int16Array = TypedArrayOf; ///< Typed-array of signed 16-bit integers using Uint16Array = TypedArrayOf; ///< Typed-array of unsigned 16-bit integers using Int32Array = TypedArrayOf; ///< Typed-array of signed 32-bit integers using Uint32Array = TypedArrayOf; ///< Typed-array of unsigned 32-bit integers using Float32Array = TypedArrayOf; ///< Typed-array of 32-bit floating-point values using Float64Array = TypedArrayOf; ///< Typed-array of 64-bit floating-point values #if NAPI_VERSION > 5 using BigInt64Array = TypedArrayOf; ///< Typed array of signed 64-bit integers using BigUint64Array = TypedArrayOf; ///< Typed array of unsigned 64-bit integers #endif // NAPI_VERSION > 5 /// Defines the signature of a Node-API C++ module's registration callback /// (init) function. using ModuleRegisterCallback = Object (*)(Env env, Object exports); class MemoryManagement; /// A simple Maybe type, representing an object which may or may not have a /// value. /// /// If an API method returns a Maybe<>, the API method can potentially fail /// either because an exception is thrown, or because an exception is pending, /// e.g. because a previous API call threw an exception that hasn't been /// caught yet. In that case, a "Nothing" value is returned. template class Maybe { public: bool IsNothing() const; bool IsJust() const; /// Short-hand for Unwrap(), which doesn't return a value. Could be used /// where the actual value of the Maybe is not needed like Object::Set. /// If this Maybe is nothing (empty), node-addon-api will crash the /// process. void Check() const; /// Return the value of type T contained in the Maybe. If this Maybe is /// nothing (empty), node-addon-api will crash the process. T Unwrap() const; /// Return the value of type T contained in the Maybe, or using a default /// value if this Maybe is nothing (empty). T UnwrapOr(const T& default_value) const; /// Converts this Maybe to a value of type T in the out. If this Maybe is /// nothing (empty), `false` is returned and `out` is left untouched. bool UnwrapTo(T* out) const; bool operator==(const Maybe& other) const; bool operator!=(const Maybe& other) const; private: Maybe(); explicit Maybe(const T& t); bool _has_value; T _value; template friend Maybe Nothing(); template friend Maybe Just(const U& u); }; template inline Maybe Nothing(); template inline Maybe Just(const T& t); #if defined(NODE_ADDON_API_ENABLE_MAYBE) template using MaybeOrValue = Maybe; #else template using MaybeOrValue = T; #endif #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER using node_addon_api_basic_env = node_api_nogc_env; using node_addon_api_basic_finalize = node_api_nogc_finalize; #else using node_addon_api_basic_env = napi_env; using node_addon_api_basic_finalize = napi_finalize; #endif /// Environment for Node-API values and operations. /// /// All Node-API values and operations must be associated with an environment. /// An environment instance is always provided to callback functions; that /// environment must then be used for any creation of Node-API values or other /// Node-API operations within the callback. (Many methods infer the /// environment from the `this` instance that the method is called on.) /// /// Multiple environments may co-exist in a single process or a thread. /// /// In the V8 JavaScript engine, a Node-API environment approximately /// corresponds to an Isolate. class BasicEnv { private: node_addon_api_basic_env _env; #if NAPI_VERSION > 5 template static void DefaultFini(Env, T* data); template static void DefaultFiniWithHint(Env, DataType* data, HintType* hint); #endif // NAPI_VERSION > 5 public: BasicEnv(node_addon_api_basic_env env); operator node_addon_api_basic_env() const; // Without these operator overloads, the error: // // Use of overloaded operator '==' is ambiguous (with operand types // 'Napi::Env' and 'Napi::Env') // // ... occurs when comparing foo.Env() == bar.Env() or foo.Env() == nullptr bool operator==(const BasicEnv& other) const { return _env == other._env; }; bool operator==(std::nullptr_t /*other*/) const { return _env == nullptr; }; #if NAPI_VERSION > 2 template class CleanupHook; template CleanupHook AddCleanupHook(Hook hook); template CleanupHook AddCleanupHook(Hook hook, Arg* arg); #endif // NAPI_VERSION > 2 #if NAPI_VERSION > 5 template T* GetInstanceData() const; template using Finalizer = void (*)(Env, T*); template fini = BasicEnv::DefaultFini> void SetInstanceData(T* data) const; template using FinalizerWithHint = void (*)(Env, DataType*, HintType*); template fini = BasicEnv::DefaultFiniWithHint> void SetInstanceData(DataType* data, HintType* hint) const; #endif // NAPI_VERSION > 5 #if NAPI_VERSION > 2 template class CleanupHook { public: CleanupHook(); CleanupHook(BasicEnv env, Hook hook, Arg* arg); CleanupHook(BasicEnv env, Hook hook); bool Remove(BasicEnv env); bool IsEmpty() const; private: static inline void Wrapper(void* data) NAPI_NOEXCEPT; static inline void WrapperWithArg(void* data) NAPI_NOEXCEPT; void (*wrapper)(void* arg); struct CleanupData { Hook hook; Arg* arg; } * data; }; #endif // NAPI_VERSION > 2 #if NAPI_VERSION > 8 const char* GetModuleFileName() const; #endif // NAPI_VERSION > 8 #ifdef NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER template inline void PostFinalizer(FinalizerType finalizeCallback) const; template inline void PostFinalizer(FinalizerType finalizeCallback, T* data) const; template inline void PostFinalizer(FinalizerType finalizeCallback, T* data, Hint* finalizeHint) const; #endif // NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER friend class Env; }; class Env : public BasicEnv { public: Env(napi_env env); operator napi_env() const; Object Global() const; Value Undefined() const; Value Null() const; bool IsExceptionPending() const; Error GetAndClearPendingException() const; MaybeOrValue RunScript(const char* utf8script) const; MaybeOrValue RunScript(const std::string& utf8script) const; MaybeOrValue RunScript(String script) const; }; /// A JavaScript value of unknown type. /// /// For type-specific operations, convert to one of the Value subclasses using a /// `To*` or `As()` method. The `To*` methods do type coercion; the `As()` /// method does not. /// /// Napi::Value value = ... /// if (!value.IsString()) throw Napi::TypeError::New(env, "Invalid /// arg..."); Napi::String str = value.As(); // Cast to a /// string value /// /// Napi::Value anotherValue = ... /// bool isTruthy = anotherValue.ToBoolean(); // Coerce to a boolean value class Value { public: Value(); ///< Creates a new _empty_ Value instance. Value(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. /// Creates a JS value from a C++ primitive. /// /// `value` may be any of: /// - bool /// - Any integer type /// - Any floating point type /// - const char* (encoded using UTF-8, null-terminated) /// - const char16_t* (encoded using UTF-16-LE, null-terminated) /// - std::string (encoded using UTF-8) /// - std::u16string /// - napi::Value /// - napi_value template static Value From(napi_env env, const T& value); static void CheckCast(napi_env env, napi_value value); /// Converts to a Node-API value primitive. /// /// If the instance is _empty_, this returns `nullptr`. operator napi_value() const; /// Tests if this value strictly equals another value. bool operator==(const Value& other) const; /// Tests if this value does not strictly equal another value. bool operator!=(const Value& other) const; /// Tests if this value strictly equals another value. bool StrictEquals(const Value& other) const; /// Gets the environment the value is associated with. Napi::Env Env() const; /// Checks if the value is empty (uninitialized). /// /// An empty value is invalid, and most attempts to perform an operation on an /// empty value will result in an exception. Note an empty value is distinct /// from JavaScript `null` or `undefined`, which are valid values. /// /// When C++ exceptions are disabled at compile time, a method with a `Value` /// return type may return an empty value to indicate a pending exception. So /// when not using C++ exceptions, callers should check whether the value is /// empty before attempting to use it. bool IsEmpty() const; napi_valuetype Type() const; ///< Gets the type of the value. bool IsUndefined() const; ///< Tests if a value is an undefined JavaScript value. bool IsNull() const; ///< Tests if a value is a null JavaScript value. bool IsBoolean() const; ///< Tests if a value is a JavaScript boolean. bool IsNumber() const; ///< Tests if a value is a JavaScript number. #if NAPI_VERSION > 5 bool IsBigInt() const; ///< Tests if a value is a JavaScript bigint. #endif // NAPI_VERSION > 5 #if (NAPI_VERSION > 4) bool IsDate() const; ///< Tests if a value is a JavaScript date. #endif bool IsString() const; ///< Tests if a value is a JavaScript string. bool IsSymbol() const; ///< Tests if a value is a JavaScript symbol. bool IsArray() const; ///< Tests if a value is a JavaScript array. bool IsArrayBuffer() const; ///< Tests if a value is a JavaScript array buffer. bool IsTypedArray() const; ///< Tests if a value is a JavaScript typed array. bool IsObject() const; ///< Tests if a value is a JavaScript object. bool IsFunction() const; ///< Tests if a value is a JavaScript function. bool IsPromise() const; ///< Tests if a value is a JavaScript promise. bool IsDataView() const; ///< Tests if a value is a JavaScript data view. bool IsBuffer() const; ///< Tests if a value is a Node buffer. bool IsExternal() const; ///< Tests if a value is a pointer to external data. /// Casts to another type of `Napi::Value`, when the actual type is known or /// assumed. /// /// This conversion does NOT coerce the type. Calling any methods /// inappropriate for the actual value type will throw `Napi::Error`. /// /// If `NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS` is defined, this method /// asserts that the actual type is the expected type. template T As() const; // Unsafe Value::As(), should be avoided. template T UnsafeAs() const; MaybeOrValue ToBoolean() const; ///< Coerces a value to a JavaScript boolean. MaybeOrValue ToNumber() const; ///< Coerces a value to a JavaScript number. MaybeOrValue ToString() const; ///< Coerces a value to a JavaScript string. MaybeOrValue ToObject() const; ///< Coerces a value to a JavaScript object. protected: /// !cond INTERNAL napi_env _env; napi_value _value; /// !endcond }; /// A JavaScript boolean value. class Boolean : public Value { public: static Boolean New(napi_env env, ///< Node-API environment bool value ///< Boolean value ); static void CheckCast(napi_env env, napi_value value); Boolean(); ///< Creates a new _empty_ Boolean instance. Boolean(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. operator bool() const; ///< Converts a Boolean value to a boolean primitive. bool Value() const; ///< Converts a Boolean value to a boolean primitive. }; /// A JavaScript number value. class Number : public Value { public: static Number New(napi_env env, ///< Node-API environment double value ///< Number value ); static void CheckCast(napi_env env, napi_value value); Number(); ///< Creates a new _empty_ Number instance. Number(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. operator int32_t() const; ///< Converts a Number value to a 32-bit signed integer value. operator uint32_t() const; ///< Converts a Number value to a 32-bit unsigned integer value. operator int64_t() const; ///< Converts a Number value to a 64-bit signed integer value. operator float() const; ///< Converts a Number value to a 32-bit floating-point value. operator double() const; ///< Converts a Number value to a 64-bit floating-point value. int32_t Int32Value() const; ///< Converts a Number value to a 32-bit signed integer value. uint32_t Uint32Value() const; ///< Converts a Number value to a 32-bit unsigned integer value. int64_t Int64Value() const; ///< Converts a Number value to a 64-bit signed integer value. float FloatValue() const; ///< Converts a Number value to a 32-bit floating-point value. double DoubleValue() const; ///< Converts a Number value to a 64-bit floating-point value. }; #if NAPI_VERSION > 5 /// A JavaScript bigint value. class BigInt : public Value { public: static BigInt New(napi_env env, ///< Node-API environment int64_t value ///< Number value ); static BigInt New(napi_env env, ///< Node-API environment uint64_t value ///< Number value ); /// Creates a new BigInt object using a specified sign bit and a /// specified list of digits/words. /// The resulting number is calculated as: /// (-1)^sign_bit * (words[0] * (2^64)^0 + words[1] * (2^64)^1 + ...) static BigInt New(napi_env env, ///< Node-API environment int sign_bit, ///< Sign bit. 1 if negative. size_t word_count, ///< Number of words in array const uint64_t* words ///< Array of words ); static void CheckCast(napi_env env, napi_value value); BigInt(); ///< Creates a new _empty_ BigInt instance. BigInt(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. int64_t Int64Value(bool* lossless) const; ///< Converts a BigInt value to a 64-bit signed integer value. uint64_t Uint64Value(bool* lossless) const; ///< Converts a BigInt value to a 64-bit unsigned integer value. size_t WordCount() const; ///< The number of 64-bit words needed to store ///< the result of ToWords(). /// Writes the contents of this BigInt to a specified memory location. /// `sign_bit` must be provided and will be set to 1 if this BigInt is /// negative. /// `*word_count` has to be initialized to the length of the `words` array. /// Upon return, it will be set to the actual number of words that would /// be needed to store this BigInt (i.e. the return value of `WordCount()`). void ToWords(int* sign_bit, size_t* word_count, uint64_t* words); }; #endif // NAPI_VERSION > 5 #if (NAPI_VERSION > 4) /// A JavaScript date value. class Date : public Value { public: /// Creates a new Date value from a double primitive. static Date New(napi_env env, ///< Node-API environment double value ///< Number value ); static void CheckCast(napi_env env, napi_value value); Date(); ///< Creates a new _empty_ Date instance. Date(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. operator double() const; ///< Converts a Date value to double primitive double ValueOf() const; ///< Converts a Date value to a double primitive. }; #endif /// A JavaScript string or symbol value (that can be used as a property name). class Name : public Value { public: static void CheckCast(napi_env env, napi_value value); Name(); ///< Creates a new _empty_ Name instance. Name(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. }; /// A JavaScript string value. class String : public Name { public: /// Creates a new String value from a UTF-8 encoded C++ string. static String New(napi_env env, ///< Node-API environment const std::string& value ///< UTF-8 encoded C++ string ); /// Creates a new String value from a UTF-16 encoded C++ string. static String New(napi_env env, ///< Node-API environment const std::u16string& value ///< UTF-16 encoded C++ string ); /// Creates a new String value from a UTF-8 encoded C string. static String New( napi_env env, ///< Node-API environment const char* value ///< UTF-8 encoded null-terminated C string ); /// Creates a new String value from a UTF-16 encoded C string. static String New( napi_env env, ///< Node-API environment const char16_t* value ///< UTF-16 encoded null-terminated C string ); /// Creates a new String value from a UTF-8 encoded C string with specified /// length. static String New(napi_env env, ///< Node-API environment const char* value, ///< UTF-8 encoded C string (not ///< necessarily null-terminated) size_t length ///< length of the string in bytes ); /// Creates a new String value from a UTF-16 encoded C string with specified /// length. static String New( napi_env env, ///< Node-API environment const char16_t* value, ///< UTF-16 encoded C string (not necessarily ///< null-terminated) size_t length ///< Length of the string in 2-byte code units ); /// Creates a new String based on the original object's type. /// /// `value` may be any of: /// - const char* (encoded using UTF-8, null-terminated) /// - const char16_t* (encoded using UTF-16-LE, null-terminated) /// - std::string (encoded using UTF-8) /// - std::u16string template static String From(napi_env env, const T& value); static void CheckCast(napi_env env, napi_value value); String(); ///< Creates a new _empty_ String instance. String(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. operator std::string() const; ///< Converts a String value to a UTF-8 encoded C++ string. operator std::u16string() const; ///< Converts a String value to a UTF-16 encoded C++ string. std::string Utf8Value() const; ///< Converts a String value to a UTF-8 encoded C++ string. std::u16string Utf16Value() const; ///< Converts a String value to a UTF-16 encoded C++ string. }; /// A JavaScript symbol value. class Symbol : public Name { public: /// Creates a new Symbol value with an optional description. static Symbol New( napi_env env, ///< Node-API environment const char* description = nullptr ///< Optional UTF-8 encoded null-terminated C string /// describing the symbol ); /// Creates a new Symbol value with a description. static Symbol New( napi_env env, ///< Node-API environment const std::string& description ///< UTF-8 encoded C++ string describing the symbol ); /// Creates a new Symbol value with a description. static Symbol New(napi_env env, ///< Node-API environment String description ///< String value describing the symbol ); /// Creates a new Symbol value with a description. static Symbol New( napi_env env, ///< Node-API environment napi_value description ///< String value describing the symbol ); /// Get a public Symbol (e.g. Symbol.iterator). static MaybeOrValue WellKnown(napi_env, const std::string& name); // Create a symbol in the global registry, UTF-8 Encoded cpp string static MaybeOrValue For(napi_env env, const std::string& description); // Create a symbol in the global registry, C style string (null terminated) static MaybeOrValue For(napi_env env, const char* description); // Create a symbol in the global registry, String value describing the symbol static MaybeOrValue For(napi_env env, String description); // Create a symbol in the global registry, napi_value describing the symbol static MaybeOrValue For(napi_env env, napi_value description); static void CheckCast(napi_env env, napi_value value); Symbol(); ///< Creates a new _empty_ Symbol instance. Symbol(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. }; class TypeTaggable : public Value { public: #if NAPI_VERSION >= 8 void TypeTag(const napi_type_tag* type_tag) const; bool CheckTypeTag(const napi_type_tag* type_tag) const; #endif // NAPI_VERSION >= 8 protected: TypeTaggable(); TypeTaggable(napi_env env, napi_value value); }; /// A JavaScript object value. class Object : public TypeTaggable { public: /// Enables property and element assignments using indexing syntax. /// /// This is a convenient helper to get and set object properties. As /// getting and setting object properties may throw with JavaScript /// exceptions, it is notable that these operations may fail. /// When NODE_ADDON_API_ENABLE_MAYBE is defined, the process will abort /// on JavaScript exceptions. /// /// Example: /// /// Napi::Value propertyValue = object1['A']; /// object2['A'] = propertyValue; /// Napi::Value elementValue = array[0]; /// array[1] = elementValue; template class PropertyLValue { public: /// Converts an L-value to a value. operator Value() const; /// Assigns a value to the property. The type of value can be /// anything supported by `Object::Set`. template PropertyLValue& operator=(ValueType value); private: PropertyLValue() = delete; PropertyLValue(Object object, Key key); napi_env _env; napi_value _object; Key _key; friend class Napi::Object; }; /// Creates a new Object value. static Object New(napi_env env ///< Node-API environment ); static void CheckCast(napi_env env, napi_value value); Object(); ///< Creates a new _empty_ Object instance. Object(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. /// Gets or sets a named property. PropertyLValue operator[]( const char* utf8name ///< UTF-8 encoded null-terminated property name ); /// Gets or sets a named property. PropertyLValue operator[]( const std::string& utf8name ///< UTF-8 encoded property name ); /// Gets or sets an indexed property or array element. PropertyLValue operator[]( uint32_t index /// Property / element index ); /// Gets or sets an indexed property or array element. PropertyLValue operator[](Value index /// Property / element index ) const; /// Gets a named property. MaybeOrValue operator[]( const char* utf8name ///< UTF-8 encoded null-terminated property name ) const; /// Gets a named property. MaybeOrValue operator[]( const std::string& utf8name ///< UTF-8 encoded property name ) const; /// Gets an indexed property or array element. MaybeOrValue operator[](uint32_t index ///< Property / element index ) const; /// Checks whether a property is present. MaybeOrValue Has(napi_value key ///< Property key primitive ) const; /// Checks whether a property is present. MaybeOrValue Has(Value key ///< Property key ) const; /// Checks whether a named property is present. MaybeOrValue Has( const char* utf8name ///< UTF-8 encoded null-terminated property name ) const; /// Checks whether a named property is present. MaybeOrValue Has( const std::string& utf8name ///< UTF-8 encoded property name ) const; /// Checks whether a own property is present. MaybeOrValue HasOwnProperty(napi_value key ///< Property key primitive ) const; /// Checks whether a own property is present. MaybeOrValue HasOwnProperty(Value key ///< Property key ) const; /// Checks whether a own property is present. MaybeOrValue HasOwnProperty( const char* utf8name ///< UTF-8 encoded null-terminated property name ) const; /// Checks whether a own property is present. MaybeOrValue HasOwnProperty( const std::string& utf8name ///< UTF-8 encoded property name ) const; /// Gets a property. MaybeOrValue Get(napi_value key ///< Property key primitive ) const; /// Gets a property. MaybeOrValue Get(Value key ///< Property key ) const; /// Gets a named property. MaybeOrValue Get( const char* utf8name ///< UTF-8 encoded null-terminated property name ) const; /// Gets a named property. MaybeOrValue Get( const std::string& utf8name ///< UTF-8 encoded property name ) const; /// Sets a property. template MaybeOrValue Set(napi_value key, ///< Property key primitive const ValueType& value ///< Property value primitive ) const; /// Sets a property. template MaybeOrValue Set(Value key, ///< Property key const ValueType& value ///< Property value ) const; /// Sets a named property. template MaybeOrValue Set( const char* utf8name, ///< UTF-8 encoded null-terminated property name const ValueType& value) const; /// Sets a named property. template MaybeOrValue Set( const std::string& utf8name, ///< UTF-8 encoded property name const ValueType& value ///< Property value primitive ) const; /// Delete property. MaybeOrValue Delete(napi_value key ///< Property key primitive ) const; /// Delete property. MaybeOrValue Delete(Value key ///< Property key ) const; /// Delete property. MaybeOrValue Delete( const char* utf8name ///< UTF-8 encoded null-terminated property name ) const; /// Delete property. MaybeOrValue Delete( const std::string& utf8name ///< UTF-8 encoded property name ) const; /// Checks whether an indexed property is present. MaybeOrValue Has(uint32_t index ///< Property / element index ) const; /// Gets an indexed property or array element. MaybeOrValue Get(uint32_t index ///< Property / element index ) const; /// Sets an indexed property or array element. template MaybeOrValue Set(uint32_t index, ///< Property / element index const ValueType& value ///< Property value primitive ) const; /// Deletes an indexed property or array element. MaybeOrValue Delete(uint32_t index ///< Property / element index ) const; /// This operation can fail in case of Proxy.[[OwnPropertyKeys]] and /// Proxy.[[GetOwnProperty]] calling into JavaScript. See: /// - /// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys /// - /// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-getownproperty-p MaybeOrValue GetPropertyNames() const; ///< Get all property names /// Defines a property on the object. /// /// This operation can fail in case of Proxy.[[DefineOwnProperty]] calling /// into JavaScript. See /// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc MaybeOrValue DefineProperty( const PropertyDescriptor& property ///< Descriptor for the property to be defined ) const; /// Defines properties on the object. /// /// This operation can fail in case of Proxy.[[DefineOwnProperty]] calling /// into JavaScript. See /// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc MaybeOrValue DefineProperties( const std::initializer_list& properties ///< List of descriptors for the properties to be defined ) const; /// Defines properties on the object. /// /// This operation can fail in case of Proxy.[[DefineOwnProperty]] calling /// into JavaScript. See /// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc MaybeOrValue DefineProperties( const std::vector& properties ///< Vector of descriptors for the properties to be defined ) const; /// Checks if an object is an instance created by a constructor function. /// /// This is equivalent to the JavaScript `instanceof` operator. /// /// This operation can fail in case of Proxy.[[GetPrototypeOf]] calling into /// JavaScript. /// See /// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-getprototypeof MaybeOrValue InstanceOf( const Function& constructor ///< Constructor function ) const; template inline void AddFinalizer(Finalizer finalizeCallback, T* data) const; template inline void AddFinalizer(Finalizer finalizeCallback, T* data, Hint* finalizeHint) const; #ifdef NODE_ADDON_API_CPP_EXCEPTIONS class const_iterator; inline const_iterator begin() const; inline const_iterator end() const; class iterator; inline iterator begin(); inline iterator end(); #endif // NODE_ADDON_API_CPP_EXCEPTIONS #if NAPI_VERSION >= 8 /// This operation can fail in case of Proxy.[[GetPrototypeOf]] calling into /// JavaScript. /// See /// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-getprototypeof MaybeOrValue Freeze() const; /// This operation can fail in case of Proxy.[[GetPrototypeOf]] calling into /// JavaScript. /// See /// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-getprototypeof MaybeOrValue Seal() const; #endif // NAPI_VERSION >= 8 }; template class External : public TypeTaggable { public: static External New(napi_env env, T* data); // Finalizer must implement `void operator()(Env env, T* data)`. template static External New(napi_env env, T* data, Finalizer finalizeCallback); // Finalizer must implement `void operator()(Env env, T* data, Hint* hint)`. template static External New(napi_env env, T* data, Finalizer finalizeCallback, Hint* finalizeHint); static void CheckCast(napi_env env, napi_value value); External(); External(napi_env env, napi_value value); T* Data() const; }; class Array : public Object { public: static Array New(napi_env env); static Array New(napi_env env, size_t length); static void CheckCast(napi_env env, napi_value value); Array(); Array(napi_env env, napi_value value); uint32_t Length() const; }; #ifdef NODE_ADDON_API_CPP_EXCEPTIONS class Object::const_iterator { private: enum class Type { BEGIN, END }; inline const_iterator(const Object* object, const Type type); public: inline const_iterator& operator++(); inline bool operator==(const const_iterator& other) const; inline bool operator!=(const const_iterator& other) const; inline const std::pair> operator*() const; private: const Napi::Object* _object; Array _keys; uint32_t _index; friend class Object; }; class Object::iterator { private: enum class Type { BEGIN, END }; inline iterator(Object* object, const Type type); public: inline iterator& operator++(); inline bool operator==(const iterator& other) const; inline bool operator!=(const iterator& other) const; inline std::pair> operator*(); private: Napi::Object* _object; Array _keys; uint32_t _index; friend class Object; }; #endif // NODE_ADDON_API_CPP_EXCEPTIONS /// A JavaScript array buffer value. class ArrayBuffer : public Object { public: /// Creates a new ArrayBuffer instance over a new automatically-allocated /// buffer. static ArrayBuffer New( napi_env env, ///< Node-API environment size_t byteLength ///< Length of the buffer to be allocated, in bytes ); #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED /// Creates a new ArrayBuffer instance, using an external buffer with /// specified byte length. static ArrayBuffer New( napi_env env, ///< Node-API environment void* externalData, ///< Pointer to the external buffer to be used by ///< the array size_t byteLength ///< Length of the external buffer to be used by the ///< array, in bytes ); /// Creates a new ArrayBuffer instance, using an external buffer with /// specified byte length. template static ArrayBuffer New( napi_env env, ///< Node-API environment void* externalData, ///< Pointer to the external buffer to be used by ///< the array size_t byteLength, ///< Length of the external buffer to be used by the ///< array, /// in bytes Finalizer finalizeCallback ///< Function to be called when the array ///< buffer is destroyed; /// must implement `void operator()(Env env, /// void* externalData)` ); /// Creates a new ArrayBuffer instance, using an external buffer with /// specified byte length. template static ArrayBuffer New( napi_env env, ///< Node-API environment void* externalData, ///< Pointer to the external buffer to be used by ///< the array size_t byteLength, ///< Length of the external buffer to be used by the ///< array, /// in bytes Finalizer finalizeCallback, ///< Function to be called when the array ///< buffer is destroyed; /// must implement `void operator()(Env /// env, void* externalData, Hint* hint)` Hint* finalizeHint ///< Hint (second parameter) to be passed to the ///< finalize callback ); #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED static void CheckCast(napi_env env, napi_value value); ArrayBuffer(); ///< Creates a new _empty_ ArrayBuffer instance. ArrayBuffer(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. void* Data(); ///< Gets a pointer to the data buffer. size_t ByteLength(); ///< Gets the length of the array buffer in bytes. #if NAPI_VERSION >= 7 bool IsDetached() const; void Detach(); #endif // NAPI_VERSION >= 7 }; /// A JavaScript typed-array value with unknown array type. /// /// For type-specific operations, cast to a `TypedArrayOf` instance using the /// `As()` method: /// /// Napi::TypedArray array = ... /// if (t.TypedArrayType() == napi_int32_array) { /// Napi::Int32Array int32Array = t.As(); /// } class TypedArray : public Object { public: static void CheckCast(napi_env env, napi_value value); TypedArray(); ///< Creates a new _empty_ TypedArray instance. TypedArray(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. napi_typedarray_type TypedArrayType() const; ///< Gets the type of this typed-array. Napi::ArrayBuffer ArrayBuffer() const; ///< Gets the backing array buffer. uint8_t ElementSize() const; ///< Gets the size in bytes of one element in the array. size_t ElementLength() const; ///< Gets the number of elements in the array. size_t ByteOffset() const; ///< Gets the offset into the buffer where the array starts. size_t ByteLength() const; ///< Gets the length of the array in bytes. protected: /// !cond INTERNAL napi_typedarray_type _type; size_t _length; TypedArray(napi_env env, napi_value value, napi_typedarray_type type, size_t length); template static #if defined(NAPI_HAS_CONSTEXPR) constexpr #endif napi_typedarray_type TypedArrayTypeForPrimitiveType() { return std::is_same::value ? napi_int8_array : std::is_same::value ? napi_uint8_array : std::is_same::value ? napi_int16_array : std::is_same::value ? napi_uint16_array : std::is_same::value ? napi_int32_array : std::is_same::value ? napi_uint32_array : std::is_same::value ? napi_float32_array : std::is_same::value ? napi_float64_array #if NAPI_VERSION > 5 : std::is_same::value ? napi_bigint64_array : std::is_same::value ? napi_biguint64_array #endif // NAPI_VERSION > 5 : napi_int8_array; } /// !endcond }; /// A JavaScript typed-array value with known array type. /// /// Note while it is possible to create and access Uint8 "clamped" arrays using /// this class, the _clamping_ behavior is only applied in JavaScript. template class TypedArrayOf : public TypedArray { public: /// Creates a new TypedArray instance over a new automatically-allocated array /// buffer. /// /// The array type parameter can normally be omitted (because it is inferred /// from the template parameter T), except when creating a "clamped" array: /// /// Uint8Array::New(env, length, napi_uint8_clamped_array) static TypedArrayOf New( napi_env env, ///< Node-API environment size_t elementLength, ///< Length of the created array, as a number of ///< elements #if defined(NAPI_HAS_CONSTEXPR) napi_typedarray_type type = TypedArray::TypedArrayTypeForPrimitiveType() #else napi_typedarray_type type #endif ///< Type of array, if different from the default array type for the ///< template parameter T. ); /// Creates a new TypedArray instance over a provided array buffer. /// /// The array type parameter can normally be omitted (because it is inferred /// from the template parameter T), except when creating a "clamped" array: /// /// Uint8Array::New(env, length, buffer, 0, napi_uint8_clamped_array) static TypedArrayOf New( napi_env env, ///< Node-API environment size_t elementLength, ///< Length of the created array, as a number of ///< elements Napi::ArrayBuffer arrayBuffer, ///< Backing array buffer instance to use size_t bufferOffset, ///< Offset into the array buffer where the ///< typed-array starts #if defined(NAPI_HAS_CONSTEXPR) napi_typedarray_type type = TypedArray::TypedArrayTypeForPrimitiveType() #else napi_typedarray_type type #endif ///< Type of array, if different from the default array type for the ///< template parameter T. ); static void CheckCast(napi_env env, napi_value value); TypedArrayOf(); ///< Creates a new _empty_ TypedArrayOf instance. TypedArrayOf(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. T& operator[](size_t index); ///< Gets or sets an element in the array. const T& operator[](size_t index) const; ///< Gets an element in the array. /// Gets a pointer to the array's backing buffer. /// /// This is not necessarily the same as the `ArrayBuffer::Data()` pointer, /// because the typed-array may have a non-zero `ByteOffset()` into the /// `ArrayBuffer`. T* Data(); /// Gets a pointer to the array's backing buffer. /// /// This is not necessarily the same as the `ArrayBuffer::Data()` pointer, /// because the typed-array may have a non-zero `ByteOffset()` into the /// `ArrayBuffer`. const T* Data() const; private: T* _data; TypedArrayOf(napi_env env, napi_value value, napi_typedarray_type type, size_t length, T* data); }; /// The DataView provides a low-level interface for reading/writing multiple /// number types in an ArrayBuffer irrespective of the platform's endianness. class DataView : public Object { public: static DataView New(napi_env env, Napi::ArrayBuffer arrayBuffer); static DataView New(napi_env env, Napi::ArrayBuffer arrayBuffer, size_t byteOffset); static DataView New(napi_env env, Napi::ArrayBuffer arrayBuffer, size_t byteOffset, size_t byteLength); static void CheckCast(napi_env env, napi_value value); DataView(); ///< Creates a new _empty_ DataView instance. DataView(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. Napi::ArrayBuffer ArrayBuffer() const; ///< Gets the backing array buffer. size_t ByteOffset() const; ///< Gets the offset into the buffer where the array starts. size_t ByteLength() const; ///< Gets the length of the array in bytes. void* Data() const; float GetFloat32(size_t byteOffset) const; double GetFloat64(size_t byteOffset) const; int8_t GetInt8(size_t byteOffset) const; int16_t GetInt16(size_t byteOffset) const; int32_t GetInt32(size_t byteOffset) const; uint8_t GetUint8(size_t byteOffset) const; uint16_t GetUint16(size_t byteOffset) const; uint32_t GetUint32(size_t byteOffset) const; void SetFloat32(size_t byteOffset, float value) const; void SetFloat64(size_t byteOffset, double value) const; void SetInt8(size_t byteOffset, int8_t value) const; void SetInt16(size_t byteOffset, int16_t value) const; void SetInt32(size_t byteOffset, int32_t value) const; void SetUint8(size_t byteOffset, uint8_t value) const; void SetUint16(size_t byteOffset, uint16_t value) const; void SetUint32(size_t byteOffset, uint32_t value) const; private: template T ReadData(size_t byteOffset) const; template void WriteData(size_t byteOffset, T value) const; void* _data{}; size_t _length{}; }; class Function : public Object { public: using VoidCallback = void (*)(const CallbackInfo& info); using Callback = Value (*)(const CallbackInfo& info); template static Function New(napi_env env, const char* utf8name = nullptr, void* data = nullptr); template static Function New(napi_env env, const char* utf8name = nullptr, void* data = nullptr); template static Function New(napi_env env, const std::string& utf8name, void* data = nullptr); template static Function New(napi_env env, const std::string& utf8name, void* data = nullptr); /// Callable must implement operator() accepting a const CallbackInfo& /// and return either void or Value. template static Function New(napi_env env, Callable cb, const char* utf8name = nullptr, void* data = nullptr); /// Callable must implement operator() accepting a const CallbackInfo& /// and return either void or Value. template static Function New(napi_env env, Callable cb, const std::string& utf8name, void* data = nullptr); static void CheckCast(napi_env env, napi_value value); Function(); Function(napi_env env, napi_value value); MaybeOrValue operator()( const std::initializer_list& args) const; MaybeOrValue Call(const std::initializer_list& args) const; MaybeOrValue Call(const std::vector& args) const; MaybeOrValue Call(const std::vector& args) const; MaybeOrValue Call(size_t argc, const napi_value* args) const; MaybeOrValue Call(napi_value recv, const std::initializer_list& args) const; MaybeOrValue Call(napi_value recv, const std::vector& args) const; MaybeOrValue Call(napi_value recv, const std::vector& args) const; MaybeOrValue Call(napi_value recv, size_t argc, const napi_value* args) const; MaybeOrValue MakeCallback( napi_value recv, const std::initializer_list& args, napi_async_context context = nullptr) const; MaybeOrValue MakeCallback(napi_value recv, const std::vector& args, napi_async_context context = nullptr) const; MaybeOrValue MakeCallback(napi_value recv, size_t argc, const napi_value* args, napi_async_context context = nullptr) const; MaybeOrValue New(const std::initializer_list& args) const; MaybeOrValue New(const std::vector& args) const; MaybeOrValue New(size_t argc, const napi_value* args) const; }; class Promise : public Object { public: class Deferred { public: static Deferred New(napi_env env); Deferred(napi_env env); Napi::Promise Promise() const; Napi::Env Env() const; void Resolve(napi_value value) const; void Reject(napi_value value) const; private: napi_env _env; napi_deferred _deferred; napi_value _promise; }; static void CheckCast(napi_env env, napi_value value); Promise(napi_env env, napi_value value); }; template class Buffer : public Uint8Array { public: static Buffer New(napi_env env, size_t length); #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED static Buffer New(napi_env env, T* data, size_t length); // Finalizer must implement `void operator()(Env env, T* data)`. template static Buffer New(napi_env env, T* data, size_t length, Finalizer finalizeCallback); // Finalizer must implement `void operator()(Env env, T* data, Hint* hint)`. template static Buffer New(napi_env env, T* data, size_t length, Finalizer finalizeCallback, Hint* finalizeHint); #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED static Buffer NewOrCopy(napi_env env, T* data, size_t length); // Finalizer must implement `void operator()(Env env, T* data)`. template static Buffer NewOrCopy(napi_env env, T* data, size_t length, Finalizer finalizeCallback); // Finalizer must implement `void operator()(Env env, T* data, Hint* hint)`. template static Buffer NewOrCopy(napi_env env, T* data, size_t length, Finalizer finalizeCallback, Hint* finalizeHint); static Buffer Copy(napi_env env, const T* data, size_t length); static void CheckCast(napi_env env, napi_value value); Buffer(); Buffer(napi_env env, napi_value value); size_t Length() const; T* Data() const; private: }; /// Holds a counted reference to a value; initially a weak reference unless /// otherwise specified, may be changed to/from a strong reference by adjusting /// the refcount. /// /// The referenced value is not immediately destroyed when the reference count /// is zero; it is merely then eligible for garbage-collection if there are no /// other references to the value. template class Reference { public: static Reference New(const T& value, uint32_t initialRefcount = 0); Reference(); Reference(napi_env env, napi_ref ref); ~Reference(); // A reference can be moved but cannot be copied. Reference(Reference&& other); Reference& operator=(Reference&& other); NAPI_DISALLOW_ASSIGN(Reference) operator napi_ref() const; bool operator==(const Reference& other) const; bool operator!=(const Reference& other) const; Napi::Env Env() const; bool IsEmpty() const; // Note when getting the value of a Reference it is usually correct to do so // within a HandleScope so that the value handle gets cleaned up efficiently. T Value() const; uint32_t Ref() const; uint32_t Unref() const; void Reset(); void Reset(const T& value, uint32_t refcount = 0); // Call this on a reference that is declared as static data, to prevent its // destructor from running at program shutdown time, which would attempt to // reset the reference when the environment is no longer valid. Avoid using // this if at all possible. If you do need to use static data, MAKE SURE to // warn your users that your addon is NOT threadsafe. void SuppressDestruct(); protected: Reference(const Reference&); /// !cond INTERNAL napi_env _env; napi_ref _ref; /// !endcond private: bool _suppressDestruct; }; class ObjectReference : public Reference { public: ObjectReference(); ObjectReference(napi_env env, napi_ref ref); // A reference can be moved but cannot be copied. ObjectReference(Reference&& other); ObjectReference& operator=(Reference&& other); ObjectReference(ObjectReference&& other); ObjectReference& operator=(ObjectReference&& other); NAPI_DISALLOW_ASSIGN(ObjectReference) MaybeOrValue Get(const char* utf8name) const; MaybeOrValue Get(const std::string& utf8name) const; MaybeOrValue Set(const char* utf8name, napi_value value) const; MaybeOrValue Set(const char* utf8name, Napi::Value value) const; MaybeOrValue Set(const char* utf8name, const char* utf8value) const; MaybeOrValue Set(const char* utf8name, bool boolValue) const; MaybeOrValue Set(const char* utf8name, double numberValue) const; MaybeOrValue Set(const std::string& utf8name, napi_value value) const; MaybeOrValue Set(const std::string& utf8name, Napi::Value value) const; MaybeOrValue Set(const std::string& utf8name, std::string& utf8value) const; MaybeOrValue Set(const std::string& utf8name, bool boolValue) const; MaybeOrValue Set(const std::string& utf8name, double numberValue) const; MaybeOrValue Get(uint32_t index) const; MaybeOrValue Set(uint32_t index, const napi_value value) const; MaybeOrValue Set(uint32_t index, const Napi::Value value) const; MaybeOrValue Set(uint32_t index, const char* utf8value) const; MaybeOrValue Set(uint32_t index, const std::string& utf8value) const; MaybeOrValue Set(uint32_t index, bool boolValue) const; MaybeOrValue Set(uint32_t index, double numberValue) const; protected: ObjectReference(const ObjectReference&); }; class FunctionReference : public Reference { public: FunctionReference(); FunctionReference(napi_env env, napi_ref ref); // A reference can be moved but cannot be copied. FunctionReference(Reference&& other); FunctionReference& operator=(Reference&& other); FunctionReference(FunctionReference&& other); FunctionReference& operator=(FunctionReference&& other); NAPI_DISALLOW_ASSIGN_COPY(FunctionReference) MaybeOrValue operator()( const std::initializer_list& args) const; MaybeOrValue Call( const std::initializer_list& args) const; MaybeOrValue Call(const std::vector& args) const; MaybeOrValue Call( napi_value recv, const std::initializer_list& args) const; MaybeOrValue Call(napi_value recv, const std::vector& args) const; MaybeOrValue Call(napi_value recv, size_t argc, const napi_value* args) const; MaybeOrValue MakeCallback( napi_value recv, const std::initializer_list& args, napi_async_context context = nullptr) const; MaybeOrValue MakeCallback( napi_value recv, const std::vector& args, napi_async_context context = nullptr) const; MaybeOrValue MakeCallback( napi_value recv, size_t argc, const napi_value* args, napi_async_context context = nullptr) const; MaybeOrValue New(const std::initializer_list& args) const; MaybeOrValue New(const std::vector& args) const; }; // Shortcuts to creating a new reference with inferred type and refcount = 0. template Reference Weak(T value); ObjectReference Weak(Object value); FunctionReference Weak(Function value); // Shortcuts to creating a new reference with inferred type and refcount = 1. template Reference Persistent(T value); ObjectReference Persistent(Object value); FunctionReference Persistent(Function value); /// A persistent reference to a JavaScript error object. Use of this class /// depends somewhat on whether C++ exceptions are enabled at compile time. /// /// ### Handling Errors With C++ Exceptions /// /// If C++ exceptions are enabled, then the `Error` class extends /// `std::exception` and enables integrated error-handling for C++ exceptions /// and JavaScript exceptions. /// /// If a Node-API call fails without executing any JavaScript code (for /// example due to an invalid argument), then the Node-API wrapper /// automatically converts and throws the error as a C++ exception of type /// `Napi::Error`. Or if a JavaScript function called by C++ code via Node-API /// throws a JavaScript exception, then the Node-API wrapper automatically /// converts and throws it as a C++ exception of type `Napi::Error`. /// /// If a C++ exception of type `Napi::Error` escapes from a Node-API C++ /// callback, then the Node-API wrapper automatically converts and throws it /// as a JavaScript exception. Therefore, catching a C++ exception of type /// `Napi::Error` prevents a JavaScript exception from being thrown. /// /// #### Example 1A - Throwing a C++ exception: /// /// Napi::Env env = ... /// throw Napi::Error::New(env, "Example exception"); /// /// Following C++ statements will not be executed. The exception will bubble /// up as a C++ exception of type `Napi::Error`, until it is either caught /// while still in C++, or else automatically propagated as a JavaScript /// exception when the callback returns to JavaScript. /// /// #### Example 2A - Propagating a Node-API C++ exception: /// /// Napi::Function jsFunctionThatThrows = someObj.As(); /// Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); /// /// Following C++ statements will not be executed. The exception will bubble /// up as a C++ exception of type `Napi::Error`, until it is either caught /// while still in C++, or else automatically propagated as a JavaScript /// exception when the callback returns to JavaScript. /// /// #### Example 3A - Handling a Node-API C++ exception: /// /// Napi::Function jsFunctionThatThrows = someObj.As(); /// Napi::Value result; /// try { /// result = jsFunctionThatThrows({ arg1, arg2 }); /// } catch (const Napi::Error& e) { /// cerr << "Caught JavaScript exception: " + e.what(); /// } /// /// Since the exception was caught here, it will not be propagated as a /// JavaScript exception. /// /// ### Handling Errors Without C++ Exceptions /// /// If C++ exceptions are disabled (by defining /// `NODE_ADDON_API_DISABLE_CPP_EXCEPTIONS`) then this class does not extend /// `std::exception`, and APIs in the `Napi` namespace do not throw C++ /// exceptions when they fail. Instead, they raise _pending_ JavaScript /// exceptions and return _empty_ `Value`s. Calling code should check /// `Value::IsEmpty()` before attempting to use a returned value, and may use /// methods on the `Env` class to check for, get, and clear a pending JavaScript /// exception. If the pending exception is not cleared, it will be thrown when /// the native callback returns to JavaScript. /// /// #### Example 1B - Throwing a JS exception /// /// Napi::Env env = ... /// Napi::Error::New(env, "Example /// exception").ThrowAsJavaScriptException(); return; /// /// After throwing a JS exception, the code should generally return /// immediately from the native callback, after performing any necessary /// cleanup. /// /// #### Example 2B - Propagating a Node-API JS exception: /// /// Napi::Function jsFunctionThatThrows = someObj.As(); /// Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); /// if (result.IsEmpty()) return; /// /// An empty value result from a Node-API call indicates an error occurred, /// and a JavaScript exception is pending. To let the exception propagate, the /// code should generally return immediately from the native callback, after /// performing any necessary cleanup. /// /// #### Example 3B - Handling a Node-API JS exception: /// /// Napi::Function jsFunctionThatThrows = someObj.As(); /// Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); /// if (result.IsEmpty()) { /// Napi::Error e = env.GetAndClearPendingException(); /// cerr << "Caught JavaScript exception: " + e.Message(); /// } /// /// Since the exception was cleared here, it will not be propagated as a /// JavaScript exception after the native callback returns. class Error : public ObjectReference #ifdef NODE_ADDON_API_CPP_EXCEPTIONS , public std::exception #endif // NODE_ADDON_API_CPP_EXCEPTIONS { public: static Error New(napi_env env); static Error New(napi_env env, const char* message); static Error New(napi_env env, const std::string& message); static NAPI_NO_RETURN void Fatal(const char* location, const char* message); Error(); Error(napi_env env, napi_value value); // An error can be moved or copied. Error(Error&& other); Error& operator=(Error&& other); Error(const Error&); Error& operator=(const Error&); const std::string& Message() const NAPI_NOEXCEPT; void ThrowAsJavaScriptException() const; Object Value() const; #ifdef NODE_ADDON_API_CPP_EXCEPTIONS const char* what() const NAPI_NOEXCEPT override; #endif // NODE_ADDON_API_CPP_EXCEPTIONS protected: /// !cond INTERNAL using create_error_fn = napi_status (*)(napi_env envb, napi_value code, napi_value msg, napi_value* result); template static TError New(napi_env env, const char* message, size_t length, create_error_fn create_error); /// !endcond private: static inline const char* ERROR_WRAP_VALUE() NAPI_NOEXCEPT; mutable std::string _message; }; class TypeError : public Error { public: static TypeError New(napi_env env, const char* message); static TypeError New(napi_env env, const std::string& message); TypeError(); TypeError(napi_env env, napi_value value); }; class RangeError : public Error { public: static RangeError New(napi_env env, const char* message); static RangeError New(napi_env env, const std::string& message); RangeError(); RangeError(napi_env env, napi_value value); }; #if NAPI_VERSION > 8 class SyntaxError : public Error { public: static SyntaxError New(napi_env env, const char* message); static SyntaxError New(napi_env env, const std::string& message); SyntaxError(); SyntaxError(napi_env env, napi_value value); }; #endif // NAPI_VERSION > 8 class CallbackInfo { public: CallbackInfo(napi_env env, napi_callback_info info); ~CallbackInfo(); // Disallow copying to prevent multiple free of _dynamicArgs NAPI_DISALLOW_ASSIGN_COPY(CallbackInfo) Napi::Env Env() const; Value NewTarget() const; bool IsConstructCall() const; size_t Length() const; const Value operator[](size_t index) const; Value This() const; void* Data() const; void SetData(void* data); explicit operator napi_callback_info() const; private: const size_t _staticArgCount = 6; napi_env _env; napi_callback_info _info; napi_value _this; size_t _argc; napi_value* _argv; napi_value _staticArgs[6]{}; napi_value* _dynamicArgs; void* _data; }; class PropertyDescriptor { public: using GetterCallback = Napi::Value (*)(const Napi::CallbackInfo& info); using SetterCallback = void (*)(const Napi::CallbackInfo& info); #ifndef NODE_ADDON_API_DISABLE_DEPRECATED template static PropertyDescriptor Accessor( const char* utf8name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( const std::string& utf8name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( napi_value name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( Name name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( const char* utf8name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( const std::string& utf8name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( napi_value name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( Name name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function( const char* utf8name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function( const std::string& utf8name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function( napi_value name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function( Name name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); #endif // !NODE_ADDON_API_DISABLE_DEPRECATED template static PropertyDescriptor Accessor( const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( const std::string& utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( Name name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( const std::string& utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( Name name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( Napi::Env env, Napi::Object object, const char* utf8name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( Napi::Env env, Napi::Object object, const std::string& utf8name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( Napi::Env env, Napi::Object object, Name name, Getter getter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( Napi::Env env, Napi::Object object, const char* utf8name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( Napi::Env env, Napi::Object object, const std::string& utf8name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Accessor( Napi::Env env, Napi::Object object, Name name, Getter getter, Setter setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function( Napi::Env env, Napi::Object object, const char* utf8name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function( Napi::Env env, Napi::Object object, const std::string& utf8name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor Function( Napi::Env env, Napi::Object object, Name name, Callable cb, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor Value( const char* utf8name, napi_value value, napi_property_attributes attributes = napi_default); static PropertyDescriptor Value( const std::string& utf8name, napi_value value, napi_property_attributes attributes = napi_default); static PropertyDescriptor Value( napi_value name, napi_value value, napi_property_attributes attributes = napi_default); static PropertyDescriptor Value( Name name, Napi::Value value, napi_property_attributes attributes = napi_default); PropertyDescriptor(napi_property_descriptor desc); operator napi_property_descriptor&(); operator const napi_property_descriptor&() const; private: napi_property_descriptor _desc; }; /// Property descriptor for use with `ObjectWrap::DefineClass()`. /// /// This is different from the standalone `PropertyDescriptor` because it is /// specific to each `ObjectWrap` subclass. This prevents using descriptors /// from a different class when defining a new class (preventing the callbacks /// from having incorrect `this` pointers). template class ClassPropertyDescriptor { public: ClassPropertyDescriptor(napi_property_descriptor desc) : _desc(desc) {} operator napi_property_descriptor&() { return _desc; } operator const napi_property_descriptor&() const { return _desc; } private: napi_property_descriptor _desc; }; template struct MethodCallbackData { TCallback callback; void* data; }; template struct AccessorCallbackData { TGetterCallback getterCallback; TSetterCallback setterCallback; void* data; }; template class InstanceWrap { public: using InstanceVoidMethodCallback = void (T::*)(const CallbackInfo& info); using InstanceMethodCallback = Napi::Value (T::*)(const CallbackInfo& info); using InstanceGetterCallback = Napi::Value (T::*)(const CallbackInfo& info); using InstanceSetterCallback = void (T::*)(const CallbackInfo& info, const Napi::Value& value); using PropertyDescriptor = ClassPropertyDescriptor; static PropertyDescriptor InstanceMethod( const char* utf8name, InstanceVoidMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceMethod( const char* utf8name, InstanceMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceMethod( Symbol name, InstanceVoidMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceMethod( Symbol name, InstanceMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceMethod( const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceMethod( const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceMethod( Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceMethod( Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceAccessor( const char* utf8name, InstanceGetterCallback getter, InstanceSetterCallback setter, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceAccessor( Symbol name, InstanceGetterCallback getter, InstanceSetterCallback setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceAccessor( const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor InstanceAccessor( Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor InstanceValue( const char* utf8name, Napi::Value value, napi_property_attributes attributes = napi_default); static PropertyDescriptor InstanceValue( Symbol name, Napi::Value value, napi_property_attributes attributes = napi_default); protected: static void AttachPropData(napi_env env, napi_value value, const napi_property_descriptor* prop); private: using This = InstanceWrap; using InstanceVoidMethodCallbackData = MethodCallbackData; using InstanceMethodCallbackData = MethodCallbackData; using InstanceAccessorCallbackData = AccessorCallbackData; static napi_value InstanceVoidMethodCallbackWrapper(napi_env env, napi_callback_info info); static napi_value InstanceMethodCallbackWrapper(napi_env env, napi_callback_info info); static napi_value InstanceGetterCallbackWrapper(napi_env env, napi_callback_info info); static napi_value InstanceSetterCallbackWrapper(napi_env env, napi_callback_info info); template static napi_value WrappedMethod(napi_env env, napi_callback_info info) NAPI_NOEXCEPT; template struct SetterTag {}; template static napi_callback WrapSetter(SetterTag) NAPI_NOEXCEPT { return &This::WrappedMethod; } static napi_callback WrapSetter(SetterTag) NAPI_NOEXCEPT { return nullptr; } }; /// Base class to be extended by C++ classes exposed to JavaScript; each C++ /// class instance gets "wrapped" by a JavaScript object that is managed by this /// class. /// /// At initialization time, the `DefineClass()` method must be used to /// hook up the accessor and method callbacks. It takes a list of /// property descriptors, which can be constructed via the various /// static methods on the base class. /// /// #### Example: /// /// class Example: public Napi::ObjectWrap { /// public: /// static void Initialize(Napi::Env& env, Napi::Object& target) { /// Napi::Function constructor = DefineClass(env, "Example", { /// InstanceAccessor<&Example::GetSomething, /// &Example::SetSomething>("value"), /// InstanceMethod<&Example::DoSomething>("doSomething"), /// }); /// target.Set("Example", constructor); /// } /// /// Example(const Napi::CallbackInfo& info); // Constructor /// Napi::Value GetSomething(const Napi::CallbackInfo& info); /// void SetSomething(const Napi::CallbackInfo& info, const Napi::Value& /// value); Napi::Value DoSomething(const Napi::CallbackInfo& info); /// } template class ObjectWrap : public InstanceWrap, public Reference { public: ObjectWrap(const CallbackInfo& callbackInfo); virtual ~ObjectWrap(); static T* Unwrap(Object wrapper); // Methods exposed to JavaScript must conform to one of these callback // signatures. using StaticVoidMethodCallback = void (*)(const CallbackInfo& info); using StaticMethodCallback = Napi::Value (*)(const CallbackInfo& info); using StaticGetterCallback = Napi::Value (*)(const CallbackInfo& info); using StaticSetterCallback = void (*)(const CallbackInfo& info, const Napi::Value& value); using PropertyDescriptor = ClassPropertyDescriptor; static Function DefineClass( Napi::Env env, const char* utf8name, const std::initializer_list& properties, void* data = nullptr); static Function DefineClass(Napi::Env env, const char* utf8name, const std::vector& properties, void* data = nullptr); static PropertyDescriptor StaticMethod( const char* utf8name, StaticVoidMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticMethod( const char* utf8name, StaticMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticMethod( Symbol name, StaticVoidMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticMethod( Symbol name, StaticMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticMethod( const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticMethod( Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticMethod( const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticMethod( Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticAccessor( const char* utf8name, StaticGetterCallback getter, StaticSetterCallback setter, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticAccessor( Symbol name, StaticGetterCallback getter, StaticSetterCallback setter, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticAccessor( const char* utf8name, napi_property_attributes attributes = napi_default, void* data = nullptr); template static PropertyDescriptor StaticAccessor( Symbol name, napi_property_attributes attributes = napi_default, void* data = nullptr); static PropertyDescriptor StaticValue( const char* utf8name, Napi::Value value, napi_property_attributes attributes = napi_default); static PropertyDescriptor StaticValue( Symbol name, Napi::Value value, napi_property_attributes attributes = napi_default); static Napi::Value OnCalledAsFunction(const Napi::CallbackInfo& callbackInfo); virtual void Finalize(Napi::Env env); virtual void Finalize(BasicEnv env); private: using This = ObjectWrap; static napi_value ConstructorCallbackWrapper(napi_env env, napi_callback_info info); static napi_value StaticVoidMethodCallbackWrapper(napi_env env, napi_callback_info info); static napi_value StaticMethodCallbackWrapper(napi_env env, napi_callback_info info); static napi_value StaticGetterCallbackWrapper(napi_env env, napi_callback_info info); static napi_value StaticSetterCallbackWrapper(napi_env env, napi_callback_info info); static void FinalizeCallback(node_addon_api_basic_env env, void* data, void* hint); static void PostFinalizeCallback(napi_env env, void* data, void* hint); static Function DefineClass(Napi::Env env, const char* utf8name, const size_t props_count, const napi_property_descriptor* props, void* data = nullptr); using StaticVoidMethodCallbackData = MethodCallbackData; using StaticMethodCallbackData = MethodCallbackData; using StaticAccessorCallbackData = AccessorCallbackData; template static napi_value WrappedMethod(napi_env env, napi_callback_info info) NAPI_NOEXCEPT; template struct StaticSetterTag {}; template static napi_callback WrapStaticSetter(StaticSetterTag) NAPI_NOEXCEPT { return &This::WrappedMethod; } static napi_callback WrapStaticSetter(StaticSetterTag) NAPI_NOEXCEPT { return nullptr; } bool _construction_failed = true; bool _finalized = false; }; class HandleScope { public: HandleScope(napi_env env, napi_handle_scope scope); explicit HandleScope(Napi::Env env); ~HandleScope(); // Disallow copying to prevent double close of napi_handle_scope NAPI_DISALLOW_ASSIGN_COPY(HandleScope) operator napi_handle_scope() const; Napi::Env Env() const; private: napi_env _env; napi_handle_scope _scope; }; class EscapableHandleScope { public: EscapableHandleScope(napi_env env, napi_escapable_handle_scope scope); explicit EscapableHandleScope(Napi::Env env); ~EscapableHandleScope(); // Disallow copying to prevent double close of napi_escapable_handle_scope NAPI_DISALLOW_ASSIGN_COPY(EscapableHandleScope) operator napi_escapable_handle_scope() const; Napi::Env Env() const; Value Escape(napi_value escapee); private: napi_env _env; napi_escapable_handle_scope _scope; }; #if (NAPI_VERSION > 2) class CallbackScope { public: CallbackScope(napi_env env, napi_callback_scope scope); CallbackScope(napi_env env, napi_async_context context); virtual ~CallbackScope(); // Disallow copying to prevent double close of napi_callback_scope NAPI_DISALLOW_ASSIGN_COPY(CallbackScope) operator napi_callback_scope() const; Napi::Env Env() const; private: napi_env _env; napi_callback_scope _scope; }; #endif class AsyncContext { public: explicit AsyncContext(napi_env env, const char* resource_name); explicit AsyncContext(napi_env env, const char* resource_name, const Object& resource); virtual ~AsyncContext(); AsyncContext(AsyncContext&& other); AsyncContext& operator=(AsyncContext&& other); NAPI_DISALLOW_ASSIGN_COPY(AsyncContext) operator napi_async_context() const; Napi::Env Env() const; private: napi_env _env; napi_async_context _context; }; #if NAPI_HAS_THREADS class AsyncWorker { public: virtual ~AsyncWorker(); NAPI_DISALLOW_ASSIGN_COPY(AsyncWorker) operator napi_async_work() const; Napi::Env Env() const; void Queue(); void Cancel(); void SuppressDestruct(); ObjectReference& Receiver(); FunctionReference& Callback(); virtual void OnExecute(Napi::Env env); virtual void OnWorkComplete(Napi::Env env, napi_status status); protected: explicit AsyncWorker(const Function& callback); explicit AsyncWorker(const Function& callback, const char* resource_name); explicit AsyncWorker(const Function& callback, const char* resource_name, const Object& resource); explicit AsyncWorker(const Object& receiver, const Function& callback); explicit AsyncWorker(const Object& receiver, const Function& callback, const char* resource_name); explicit AsyncWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource); explicit AsyncWorker(Napi::Env env); explicit AsyncWorker(Napi::Env env, const char* resource_name); explicit AsyncWorker(Napi::Env env, const char* resource_name, const Object& resource); virtual void Execute() = 0; virtual void OnOK(); virtual void OnError(const Error& e); virtual void Destroy(); virtual std::vector GetResult(Napi::Env env); void SetError(const std::string& error); private: static inline void OnAsyncWorkExecute(napi_env env, void* asyncworker); static inline void OnAsyncWorkComplete(napi_env env, napi_status status, void* asyncworker); napi_env _env; napi_async_work _work; ObjectReference _receiver; FunctionReference _callback; std::string _error; bool _suppress_destruct; }; #endif // NAPI_HAS_THREADS #if (NAPI_VERSION > 3 && NAPI_HAS_THREADS) class ThreadSafeFunction { public: // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback, FinalizerDataType* data); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, Finalizer finalizeCallback, FinalizerDataType* data); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback); // This API may only be called from the main thread. template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data); ThreadSafeFunction(); ThreadSafeFunction(napi_threadsafe_function tsFunctionValue); operator napi_threadsafe_function() const; // This API may be called from any thread. napi_status BlockingCall() const; // This API may be called from any thread. template napi_status BlockingCall(Callback callback) const; // This API may be called from any thread. template napi_status BlockingCall(DataType* data, Callback callback) const; // This API may be called from any thread. napi_status NonBlockingCall() const; // This API may be called from any thread. template napi_status NonBlockingCall(Callback callback) const; // This API may be called from any thread. template napi_status NonBlockingCall(DataType* data, Callback callback) const; // This API may only be called from the main thread. void Ref(napi_env env) const; // This API may only be called from the main thread. void Unref(napi_env env) const; // This API may be called from any thread. napi_status Acquire() const; // This API may be called from any thread. napi_status Release() const; // This API may be called from any thread. napi_status Abort() const; struct ConvertibleContext { template operator T*() { return static_cast(context); } void* context; }; // This API may be called from any thread. ConvertibleContext GetContext() const; private: using CallbackWrapper = std::function; template static ThreadSafeFunction New(napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data, napi_finalize wrapper); napi_status CallInternal(CallbackWrapper* callbackWrapper, napi_threadsafe_function_call_mode mode) const; static void CallJS(napi_env env, napi_value jsCallback, void* context, void* data); napi_threadsafe_function _tsfn; }; // A TypedThreadSafeFunction by default has no context (nullptr) and can // accept any type (void) to its CallJs. template class TypedThreadSafeFunction { public: // This API may only be called from the main thread. // Helper function that returns nullptr if running Node-API 5+, otherwise a // non-empty, no-op Function. This provides the ability to specify at // compile-time a callback parameter to `New` that safely does no action // when targeting _any_ Node-API version. #if NAPI_VERSION > 4 static std::nullptr_t EmptyFunctionFactory(Napi::Env env); #else static Napi::Function EmptyFunctionFactory(Napi::Env env); #endif static Napi::Function FunctionOrEmpty(Napi::Env env, Napi::Function& callback); #if NAPI_VERSION > 4 // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [missing] Resource [missing] Finalizer [missing] template static TypedThreadSafeFunction New( napi_env env, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [missing] Resource [passed] Finalizer [missing] template static TypedThreadSafeFunction New( napi_env env, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [missing] Resource [missing] Finalizer [passed] template static TypedThreadSafeFunction New( napi_env env, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [missing] Resource [passed] Finalizer [passed] template static TypedThreadSafeFunction New( napi_env env, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data = nullptr); #endif // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [passed] Resource [missing] Finalizer [missing] template static TypedThreadSafeFunction New( napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [passed] Resource [passed] Finalizer [missing] template static TypedThreadSafeFunction New( napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [passed] Resource [missing] Finalizer [passed] template static TypedThreadSafeFunction New( napi_env env, const Function& callback, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data = nullptr); // This API may only be called from the main thread. // Creates a new threadsafe function with: // Callback [passed] Resource [passed] Finalizer [passed] template static TypedThreadSafeFunction New( napi_env env, CallbackType callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data = nullptr); TypedThreadSafeFunction(); TypedThreadSafeFunction(napi_threadsafe_function tsFunctionValue); operator napi_threadsafe_function() const; // This API may be called from any thread. napi_status BlockingCall(DataType* data = nullptr) const; // This API may be called from any thread. napi_status NonBlockingCall(DataType* data = nullptr) const; // This API may only be called from the main thread. void Ref(napi_env env) const; // This API may only be called from the main thread. void Unref(napi_env env) const; // This API may be called from any thread. napi_status Acquire() const; // This API may be called from any thread. napi_status Release() const; // This API may be called from any thread. napi_status Abort() const; // This API may be called from any thread. ContextType* GetContext() const; private: template static TypedThreadSafeFunction New( napi_env env, const Function& callback, const Object& resource, ResourceString resourceName, size_t maxQueueSize, size_t initialThreadCount, ContextType* context, Finalizer finalizeCallback, FinalizerDataType* data, napi_finalize wrapper); static void CallJsInternal(napi_env env, napi_value jsCallback, void* context, void* data); protected: napi_threadsafe_function _tsfn; }; template class AsyncProgressWorkerBase : public AsyncWorker { public: virtual void OnWorkProgress(DataType* data) = 0; class ThreadSafeData { public: ThreadSafeData(AsyncProgressWorkerBase* asyncprogressworker, DataType* data) : _asyncprogressworker(asyncprogressworker), _data(data) {} AsyncProgressWorkerBase* asyncprogressworker() { return _asyncprogressworker; }; DataType* data() { return _data; }; private: AsyncProgressWorkerBase* _asyncprogressworker; DataType* _data; }; void OnWorkComplete(Napi::Env env, napi_status status) override; protected: explicit AsyncProgressWorkerBase(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource, size_t queue_size = 1); virtual ~AsyncProgressWorkerBase(); // Optional callback of Napi::ThreadSafeFunction only available after // NAPI_VERSION 4. Refs: https://github.com/nodejs/node/pull/27791 #if NAPI_VERSION > 4 explicit AsyncProgressWorkerBase(Napi::Env env, const char* resource_name, const Object& resource, size_t queue_size = 1); #endif static inline void OnAsyncWorkProgress(Napi::Env env, Napi::Function jsCallback, void* data); napi_status NonBlockingCall(DataType* data); private: ThreadSafeFunction _tsfn; bool _work_completed = false; napi_status _complete_status; static inline void OnThreadSafeFunctionFinalize( Napi::Env env, void* data, AsyncProgressWorkerBase* context); }; template class AsyncProgressWorker : public AsyncProgressWorkerBase { public: virtual ~AsyncProgressWorker(); class ExecutionProgress { friend class AsyncProgressWorker; public: void Signal() const; void Send(const T* data, size_t count) const; private: explicit ExecutionProgress(AsyncProgressWorker* worker) : _worker(worker) {} AsyncProgressWorker* const _worker; }; void OnWorkProgress(void*) override; protected: explicit AsyncProgressWorker(const Function& callback); explicit AsyncProgressWorker(const Function& callback, const char* resource_name); explicit AsyncProgressWorker(const Function& callback, const char* resource_name, const Object& resource); explicit AsyncProgressWorker(const Object& receiver, const Function& callback); explicit AsyncProgressWorker(const Object& receiver, const Function& callback, const char* resource_name); explicit AsyncProgressWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource); // Optional callback of Napi::ThreadSafeFunction only available after // NAPI_VERSION 4. Refs: https://github.com/nodejs/node/pull/27791 #if NAPI_VERSION > 4 explicit AsyncProgressWorker(Napi::Env env); explicit AsyncProgressWorker(Napi::Env env, const char* resource_name); explicit AsyncProgressWorker(Napi::Env env, const char* resource_name, const Object& resource); #endif virtual void Execute(const ExecutionProgress& progress) = 0; virtual void OnProgress(const T* data, size_t count) = 0; private: void Execute() override; void Signal(); void SendProgress_(const T* data, size_t count); std::mutex _mutex; T* _asyncdata; size_t _asyncsize; bool _signaled; }; template class AsyncProgressQueueWorker : public AsyncProgressWorkerBase> { public: virtual ~AsyncProgressQueueWorker(){}; class ExecutionProgress { friend class AsyncProgressQueueWorker; public: void Signal() const; void Send(const T* data, size_t count) const; private: explicit ExecutionProgress(AsyncProgressQueueWorker* worker) : _worker(worker) {} AsyncProgressQueueWorker* const _worker; }; void OnWorkComplete(Napi::Env env, napi_status status) override; void OnWorkProgress(std::pair*) override; protected: explicit AsyncProgressQueueWorker(const Function& callback); explicit AsyncProgressQueueWorker(const Function& callback, const char* resource_name); explicit AsyncProgressQueueWorker(const Function& callback, const char* resource_name, const Object& resource); explicit AsyncProgressQueueWorker(const Object& receiver, const Function& callback); explicit AsyncProgressQueueWorker(const Object& receiver, const Function& callback, const char* resource_name); explicit AsyncProgressQueueWorker(const Object& receiver, const Function& callback, const char* resource_name, const Object& resource); // Optional callback of Napi::ThreadSafeFunction only available after // NAPI_VERSION 4. Refs: https://github.com/nodejs/node/pull/27791 #if NAPI_VERSION > 4 explicit AsyncProgressQueueWorker(Napi::Env env); explicit AsyncProgressQueueWorker(Napi::Env env, const char* resource_name); explicit AsyncProgressQueueWorker(Napi::Env env, const char* resource_name, const Object& resource); #endif virtual void Execute(const ExecutionProgress& progress) = 0; virtual void OnProgress(const T* data, size_t count) = 0; private: void Execute() override; void Signal() const; void SendProgress_(const T* data, size_t count); }; #endif // NAPI_VERSION > 3 && NAPI_HAS_THREADS // Memory management. class MemoryManagement { public: static int64_t AdjustExternalMemory(BasicEnv env, int64_t change_in_bytes); }; // Version management class VersionManagement { public: static uint32_t GetNapiVersion(BasicEnv env); static const napi_node_version* GetNodeVersion(BasicEnv env); }; #if NAPI_VERSION > 5 template class Addon : public InstanceWrap { public: static inline Object Init(Env env, Object exports); static T* Unwrap(Object wrapper); protected: using AddonProp = ClassPropertyDescriptor; void DefineAddon(Object exports, const std::initializer_list& props); Napi::Object DefineProperties(Object object, const std::initializer_list& props); private: Object entry_point_; }; #endif // NAPI_VERSION > 5 #ifdef NAPI_CPP_CUSTOM_NAMESPACE } // namespace NAPI_CPP_CUSTOM_NAMESPACE #endif } // namespace Napi // Inline implementations of all the above class methods are included here. #include "napi-inl.h" #endif // SRC_NAPI_H_ { 'targets': [ { 'target_name': 'node_addon_api', 'type': 'none', 'sources': [ 'napi.h', 'napi-inl.h' ], 'direct_dependent_settings': { 'include_dirs': [ '.' ], 'includes': ['noexcept.gypi'], } }, { 'target_name': 'node_addon_api_except', 'type': 'none', 'sources': [ 'napi.h', 'napi-inl.h' ], 'direct_dependent_settings': { 'include_dirs': [ '.' ], 'includes': ['except.gypi'], } }, { 'target_name': 'node_addon_api_except_all', 'type': 'none', 'sources': [ 'napi.h', 'napi-inl.h' ], 'direct_dependent_settings': { 'include_dirs': [ '.' ], 'includes': ['except.gypi'], 'defines': [ 'NODE_ADDON_API_CPP_EXCEPTIONS_ALL' ] } }, { 'target_name': 'node_addon_api_maybe', 'type': 'none', 'sources': [ 'napi.h', 'napi-inl.h' ], 'direct_dependent_settings': { 'include_dirs': [ '.' ], 'includes': ['noexcept.gypi'], 'defines': ['NODE_ADDON_API_ENABLE_MAYBE'] } }, ] } { 'targets': [ { 'target_name': 'nothing', 'type': 'static_library', 'sources': [ 'nothing.c' ] } ] } { 'defines': [ 'NODE_ADDON_API_DISABLE_CPP_EXCEPTIONS' ], 'cflags': [ '-fno-exceptions' ], 'cflags_cc': [ '-fno-exceptions' ], 'conditions': [ ["OS=='win'", { # _HAS_EXCEPTIONS is already defined and set to 0 in common.gypi #"defines": [ # "_HAS_EXCEPTIONS=0" #], "msvs_settings": { "VCCLCompilerTool": { 'ExceptionHandling': 0, 'EnablePREfast': 'true', }, }, }], ["OS=='mac'", { 'xcode_settings': { 'CLANG_CXX_LIBRARY': 'libc++', 'MACOSX_DEPLOYMENT_TARGET': '10.7', 'GCC_ENABLE_CPP_EXCEPTIONS': 'NO', }, }], ], } { "versions": [ { "version": "*", "target": { "node": "active" }, "response": { "type": "time-permitting", "paid": false, "contact": { "name": "node-addon-api team", "url": "https://github.com/nodejs/node-addon-api/issues" } }, "backing": [ { "project": "https://github.com/nodejs" }, { "foundation": "https://openjsf.org/" } ] } ] } { "description": "Node.js API (Node-API)", "devDependencies": { "benchmark": "^2.1.4", "bindings": "^1.5.0", "clang-format": "^1.4.0", "eslint": "^9.13.0", "fs-extra": "^11.1.1", "neostandard": "^0.11.7", "pre-commit": "^1.2.2", "semver": "^7.6.0" }, "directories": {}, "gypfile": false, "homepage": "https://github.com/nodejs/node-addon-api", "license": "MIT", "main": "index.js", "name": "node-addon-api", "readme": "README.md", "repository": { "type": "git", "url": "git://github.com/nodejs/node-addon-api.git" }, "files": [ "*.{c,h,gyp,gypi}", "package-support.json", "tools/" ], "pre-commit": "lint", "version": "8.3.0", "support": true, "engines": { "node": "^18 || ^20 || >= 21" } }# Tools ## clang-format The clang-format checking tools is designed to check changed lines of code compared to given git-refs. ## Migration Script The migration tool is designed to reduce repetitive work in the migration process. However, the script is not aiming to convert every thing for you. There are usually some small fixes and major reconstruction required. ### How To Use To run the conversion script, first make sure you have the latest `node-addon-api` in your `node_modules` directory. ``` npm install node-addon-api ``` Then run the script passing your project directory ``` node ./node_modules/node-addon-api/tools/conversion.js ./ ``` After finish, recompile and debug things that are missed by the script. ### Quick Fixes Here is the list of things that can be fixed easily. 1. Change your methods' return value to void if it doesn't return value to JavaScript. 2. Use `.` to access attribute or to invoke member function in Napi::Object instead of `->`. 3. `Napi::New(env, value);` to `Napi::[Type]::New(env, value); ### Major Reconstructions The implementation of `Napi::ObjectWrap` is significantly different from NAN's. `Napi::ObjectWrap` takes a pointer to the wrapped object and creates a reference to the wrapped object inside ObjectWrap constructor. `Napi::ObjectWrap` also associates wrapped object's instance methods to Javascript module instead of static methods like NAN. So if you use Nan::ObjectWrap in your module, you will need to execute the following steps. 1. Convert your [ClassName]::New function to a constructor function that takes a `Napi::CallbackInfo`. Declare it as ``` [ClassName](const Napi::CallbackInfo& info); ``` and define it as ``` [ClassName]::[ClassName](const Napi::CallbackInfo& info) : Napi::ObjectWrap<[ClassName]>(info){ ... } ``` This way, the `Napi::ObjectWrap` constructor will be invoked after the object has been instantiated and `Napi::ObjectWrap` can use the `this` pointer to create a reference to the wrapped object. 2. Move your original constructor code into the new constructor. Delete your original constructor. 3. In your class initialization function, associate native methods in the following way. ``` Napi::FunctionReference constructor; void [ClassName]::Init(Napi::Env env, Napi::Object exports, Napi::Object module) { Napi::HandleScope scope(env); Napi::Function ctor = DefineClass(env, "Canvas", { InstanceMethod<&[ClassName]::Func1>("Func1"), InstanceMethod<&[ClassName]::Func2>("Func2"), InstanceAccessor<&[ClassName]::ValueGetter>("Value"), StaticMethod<&[ClassName]::StaticMethod>("MethodName"), InstanceValue("Value", Napi::[Type]::New(env, value)), }); constructor = Napi::Persistent(ctor); constructor .SuppressDestruct(); exports.Set("[ClassName]", ctor); } ``` 4. In function where you need to Unwrap the ObjectWrap in NAN like `[ClassName]* native = Nan::ObjectWrap::Unwrap<[ClassName]>(info.This());`, use `this` pointer directly as the unwrapped object as each ObjectWrap instance is associated with a unique object instance. If you still find issues after following this guide, please leave us an issue describing your problem and we will try to resolve it. 'use strict'; // Descend into a directory structure and, for each file matching *.node, output // based on the imports found in the file whether it's an N-API module or not. const fs = require('fs'); const path = require('path'); // Read the output of the command, break it into lines, and use the reducer to // decide whether the file is an N-API module or not. function checkFile (file, command, argv, reducer) { const child = require('child_process').spawn(command, argv, { stdio: ['inherit', 'pipe', 'inherit'] }); let leftover = ''; let isNapi; child.stdout.on('data', (chunk) => { if (isNapi === undefined) { chunk = (leftover + chunk.toString()).split(/[\r\n]+/); leftover = chunk.pop(); isNapi = chunk.reduce(reducer, isNapi); if (isNapi !== undefined) { child.kill(); } } }); child.on('close', (code, signal) => { if ((code === null && signal !== null) || (code !== 0)) { console.log( command + ' exited with code: ' + code + ' and signal: ' + signal); } else { // Green if it's a N-API module, red otherwise. console.log( '\x1b[' + (isNapi ? '42' : '41') + 'm' + (isNapi ? ' N-API' : 'Not N-API') + '\x1b[0m: ' + file); } }); } // Use nm -a to list symbols. function checkFileUNIX (file) { checkFile(file, 'nm', ['-a', file], (soFar, line) => { if (soFar === undefined) { line = line.match(/([0-9a-f]*)? ([a-zA-Z]) (.*$)/); if (line[2] === 'U') { if (/^napi/.test(line[3])) { soFar = true; } } } return soFar; }); } // Use dumpbin /imports to list symbols. function checkFileWin32 (file) { checkFile(file, 'dumpbin', ['/imports', file], (soFar, line) => { if (soFar === undefined) { line = line.match(/([0-9a-f]*)? +([a-zA-Z0-9]) (.*$)/); if (line && /^napi/.test(line[line.length - 1])) { soFar = true; } } return soFar; }); } // Descend into a directory structure and pass each file ending in '.node' to // one of the above checks, depending on the OS. function recurse (top) { fs.readdir(top, (error, items) => { if (error) { throw new Error('error reading directory ' + top + ': ' + error); } items.forEach((item) => { item = path.join(top, item); fs.stat(item, ((item) => (error, stats) => { if (error) { throw new Error('error about ' + item + ': ' + error); } if (stats.isDirectory()) { recurse(item); } else if (/[.]node$/.test(item) && // Explicitly ignore files called 'nothing.node' because they are // artefacts of node-addon-api having identified a version of // Node.js that ships with a correct implementation of N-API. path.basename(item) !== 'nothing.node') { process.platform === 'win32' ? checkFileWin32(item) : checkFileUNIX(item); } })(item)); }); }); } // Start with the directory given on the command line or the current directory // if nothing was given. recurse(process.argv.length > 3 ? process.argv[2] : '.'); #!/usr/bin/env node const spawn = require('child_process').spawnSync; const path = require('path'); const filesToCheck = ['*.h', '*.cc']; const FORMAT_START = process.env.FORMAT_START || 'main'; function main (args) { let fix = false; while (args.length > 0) { switch (args[0]) { case '-f': case '--fix': fix = true; break; default: } args.shift(); } const clangFormatPath = path.dirname(require.resolve('clang-format')); const binary = process.platform === 'win32' ? 'node_modules\\.bin\\clang-format.cmd' : 'node_modules/.bin/clang-format'; const options = ['--binary=' + binary, '--style=file']; if (fix) { options.push(FORMAT_START); } else { options.push('--diff', FORMAT_START); } const gitClangFormatPath = path.join(clangFormatPath, 'bin/git-clang-format'); const result = spawn( 'python', [gitClangFormatPath, ...options, '--', ...filesToCheck], { encoding: 'utf-8' } ); if (result.stderr) { console.error('Error running git-clang-format:', result.stderr); return 2; } const clangFormatOutput = result.stdout.trim(); // Bail fast if in fix mode. if (fix) { console.log(clangFormatOutput); return 0; } // Detect if there is any complains from clang-format if ( clangFormatOutput !== '' && clangFormatOutput !== 'no modified files to format' && clangFormatOutput !== 'clang-format did not modify any files' ) { console.error(clangFormatOutput); const fixCmd = 'npm run lint:fix'; console.error(` ERROR: please run "${fixCmd}" to format changes in your commit Note that when running the command locally, please keep your local main branch and working branch up to date with nodejs/node-addon-api to exclude un-related complains. Or you can run "env FORMAT_START=upstream/main ${fixCmd}".`); return 1; } } if (require.main === module) { process.exitCode = main(process.argv.slice(2)); } #! /usr/bin/env node 'use strict'; const fs = require('fs'); const path = require('path'); const args = process.argv.slice(2); const dir = args[0]; if (!dir) { console.log('Usage: node ' + path.basename(__filename) + ' '); process.exit(1); } const NodeApiVersion = require('../').version; const disable = args[1]; let ConfigFileOperations; if (disable !== '--disable' && dir !== '--disable') { ConfigFileOperations = { 'package.json': [ [/([ ]*)"dependencies": {/g, '$1"dependencies": {\n$1 "node-addon-api": "' + NodeApiVersion + '",'], [/[ ]*"nan": *"[^"]+"(,|)[\n\r]/g, ''] ], 'binding.gyp': [ [/([ ]*)'include_dirs': \[/g, '$1\'include_dirs\': [\n$1 \'\s+(\w+)\s*=\s*Nan::New\([\w\d:]+\);(?:\w+->Reset\(\1\))?\s+\1->SetClassName\(Nan::String::New\("(\w+)"\)\);/g, 'Napi::Function $1 = DefineClass(env, "$2", {'], [/Local\s+(\w+)\s*=\s*Nan::New\([\w\d:]+\);\s+(\w+)\.Reset\((\1)\);\s+\1->SetClassName\((Nan::String::New|Nan::New<(v8::)*String>)\("(.+?)"\)\);/g, 'Napi::Function $1 = DefineClass(env, "$6", {'], [/Local\s+(\w+)\s*=\s*Nan::New\([\w\d:]+\);(?:\w+->Reset\(\1\))?\s+\1->SetClassName\(Nan::String::New\("(\w+)"\)\);/g, 'Napi::Function $1 = DefineClass(env, "$2", {'], [/Nan::New\(([\w\d:]+)\)->GetFunction\(\)/g, 'Napi::Function::New(env, $1)'], [/Nan::New\(([\w\d:]+)\)->GetFunction()/g, 'Napi::Function::New(env, $1);'], [/Nan::New\(([\w\d:]+)\)/g, 'Napi::Function::New(env, $1)'], [/Nan::New\(([\w\d:]+)\)/g, 'Napi::Function::New(env, $1)'], // FunctionTemplate to FunctionReference [/Nan::Persistent<(v8::)*FunctionTemplate>/g, 'Napi::FunctionReference'], [/Nan::Persistent<(v8::)*Function>/g, 'Napi::FunctionReference'], [/v8::Local/g, 'Napi::FunctionReference'], [/Local/g, 'Napi::FunctionReference'], [/v8::FunctionTemplate/g, 'Napi::FunctionReference'], [/FunctionTemplate/g, 'Napi::FunctionReference'], [/([ ]*)Nan::SetPrototypeMethod\(\w+, "(\w+)", (\w+)\);/g, '$1InstanceMethod("$2", &$3),'], [/([ ]*)(?:\w+\.Reset\(\w+\);\s+)?\(target\)\.Set\("(\w+)",\s*Nan::GetFunction\((\w+)\)\);/gm, '});\n\n' + '$1constructor = Napi::Persistent($3);\n' + '$1constructor.SuppressDestruct();\n' + '$1target.Set("$2", $3);'], // TODO: Other attribute combinations [/static_cast\(ReadOnly\s*\|\s*DontDelete\)/gm, 'static_cast(napi_enumerable | napi_configurable)'], [/([\w\d:<>]+?)::Cast\((.+?)\)/g, '$2.As<$1>()'], [/\*Nan::Utf8String\(([^)]+)\)/g, '$1->As().Utf8Value().c_str()'], [/Nan::Utf8String +(\w+)\(([^)]+)\)/g, 'std::string $1 = $2.As()'], [/Nan::Utf8String/g, 'std::string'], [/v8::String::Utf8Value (.+?)\((.+?)\)/g, 'Napi::String $1(env, $2)'], [/String::Utf8Value (.+?)\((.+?)\)/g, 'Napi::String $1(env, $2)'], [/\.length\(\)/g, '.Length()'], [/Nan::MakeCallback\(([^,]+),[\s\\]+([^,]+),/gm, '$2.MakeCallback($1,'], [/class\s+(\w+)\s*:\s*public\s+Nan::ObjectWrap/g, 'class $1 : public Napi::ObjectWrap<$1>'], [/(\w+)\(([^)]*)\)\s*:\s*Nan::ObjectWrap\(\)\s*(,)?/gm, '$1($2) : Napi::ObjectWrap<$1>()$3'], // HandleOKCallback to OnOK [/HandleOKCallback/g, 'OnOK'], // HandleErrorCallback to OnError [/HandleErrorCallback/g, 'OnError'], // ex. .As() to .As() [/\.As\(\)/g, '.As()'], [/\.As<(Value|Boolean|String|Number|Object|Array|Symbol|External|Function)>\(\)/g, '.As()'], // ex. Nan::New(info[0]) to Napi::Number::New(info[0]) [/Nan::New<(v8::)*Integer>\((.+?)\)/g, 'Napi::Number::New(env, $2)'], [/Nan::New\(([0-9.]+)\)/g, 'Napi::Number::New(env, $1)'], [/Nan::New<(v8::)*String>\("(.+?)"\)/g, 'Napi::String::New(env, "$2")'], [/Nan::New\("(.+?)"\)/g, 'Napi::String::New(env, "$1")'], [/Nan::New<(v8::)*(.+?)>\(\)/g, 'Napi::$2::New(env)'], [/Nan::New<(.+?)>\(\)/g, 'Napi::$1::New(env)'], [/Nan::New<(v8::)*(.+?)>\(/g, 'Napi::$2::New(env, '], [/Nan::New<(.+?)>\(/g, 'Napi::$1::New(env, '], [/Nan::NewBuffer\(/g, 'Napi::Buffer::New(env, '], // TODO: Properly handle this [/Nan::New\(/g, 'Napi::New(env, '], [/\.IsInt32\(\)/g, '.IsNumber()'], [/->IsInt32\(\)/g, '.IsNumber()'], [/(.+?)->BooleanValue\(\)/g, '$1.As().Value()'], [/(.+?)->Int32Value\(\)/g, '$1.As().Int32Value()'], [/(.+?)->Uint32Value\(\)/g, '$1.As().Uint32Value()'], [/(.+?)->IntegerValue\(\)/g, '$1.As().Int64Value()'], [/(.+?)->NumberValue\(\)/g, '$1.As().DoubleValue()'], // ex. Nan::To(info[0]) to info[0].Value() [/Nan::To\((.+?)\)/g, '$2.To()'], [/Nan::To<(Boolean|String|Number|Object|Array|Symbol|Function)>\((.+?)\)/g, '$2.To()'], // ex. Nan::To(info[0]) to info[0].As().Value() [/Nan::To\((.+?)\)/g, '$1.As().Value()'], // ex. Nan::To(info[0]) to info[0].As().Int32Value() [/Nan::To\((.+?)\)/g, '$1.As().Int32Value()'], // ex. Nan::To(info[0]) to info[0].As().Int32Value() [/Nan::To\((.+?)\)/g, '$1.As().Int32Value()'], // ex. Nan::To(info[0]) to info[0].As().Uint32Value() [/Nan::To\((.+?)\)/g, '$1.As().Uint32Value()'], // ex. Nan::To(info[0]) to info[0].As().Int64Value() [/Nan::To\((.+?)\)/g, '$1.As().Int64Value()'], // ex. Nan::To(info[0]) to info[0].As().FloatValue() [/Nan::To\((.+?)\)/g, '$1.As().FloatValue()'], // ex. Nan::To(info[0]) to info[0].As().DoubleValue() [/Nan::To\((.+?)\)/g, '$1.As().DoubleValue()'], [/Nan::New\((\w+)\)->HasInstance\((\w+)\)/g, '$2.InstanceOf($1.Value())'], [/Nan::Has\(([^,]+),\s*/gm, '($1).Has('], [/\.Has\([\s|\\]*Nan::New<(v8::)*String>\(([^)]+)\)\)/gm, '.Has($1)'], [/\.Has\([\s|\\]*Nan::New\(([^)]+)\)\)/gm, '.Has($1)'], [/Nan::Get\(([^,]+),\s*/gm, '($1).Get('], [/\.Get\([\s|\\]*Nan::New<(v8::)*String>\(([^)]+)\)\)/gm, '.Get($1)'], [/\.Get\([\s|\\]*Nan::New\(([^)]+)\)\)/gm, '.Get($1)'], [/Nan::Set\(([^,]+),\s*/gm, '($1).Set('], [/\.Set\([\s|\\]*Nan::New<(v8::)*String>\(([^)]+)\)\s*,/gm, '.Set($1,'], [/\.Set\([\s|\\]*Nan::New\(([^)]+)\)\s*,/gm, '.Set($1,'], // ex. node::Buffer::HasInstance(info[0]) to info[0].IsBuffer() [/node::Buffer::HasInstance\((.+?)\)/g, '$1.IsBuffer()'], // ex. node::Buffer::Length(info[0]) to info[0].Length() [/node::Buffer::Length\((.+?)\)/g, '$1.As>().Length()'], // ex. node::Buffer::Data(info[0]) to info[0].Data() [/node::Buffer::Data\((.+?)\)/g, '$1.As>().Data()'], [/Nan::CopyBuffer\(/g, 'Napi::Buffer::Copy(env, '], // Nan::AsyncQueueWorker(worker) [/Nan::AsyncQueueWorker\((.+)\);/g, '$1.Queue();'], [/Nan::(Undefined|Null|True|False)\(\)/g, 'env.$1()'], // Nan::ThrowError(error) to Napi::Error::New(env, error).ThrowAsJavaScriptException() [/([ ]*)return Nan::Throw(\w*?)Error\((.+?)\);/g, '$1Napi::$2Error::New(env, $3).ThrowAsJavaScriptException();\n$1return env.Null();'], [/Nan::Throw(\w*?)Error\((.+?)\);\n(\s*)return;/g, 'Napi::$1Error::New(env, $2).ThrowAsJavaScriptException();\n$3return env.Null();'], [/Nan::Throw(\w*?)Error\((.+?)\);/g, 'Napi::$1Error::New(env, $2).ThrowAsJavaScriptException();\n'], // Nan::RangeError(error) to Napi::RangeError::New(env, error) [/Nan::(\w*?)Error\((.+)\)/g, 'Napi::$1Error::New(env, $2)'], [/Nan::Set\((.+?),\n* *(.+?),\n* *(.+?),\n* *(.+?)\)/g, '$1.Set($2, $3, $4)'], [/Nan::(Escapable)?HandleScope\s+(\w+)\s*;/g, 'Napi::$1HandleScope $2(env);'], [/Nan::(Escapable)?HandleScope/g, 'Napi::$1HandleScope'], [/Nan::ForceSet\(([^,]+), ?/g, '$1->DefineProperty('], [/\.ForceSet\(Napi::String::New\(env, "(\w+)"\),\s*?/g, '.DefineProperty("$1", '], // [ /Nan::GetPropertyNames\(([^,]+)\)/, '$1->GetPropertyNames()' ], [/Nan::Equals\(([^,]+),/g, '$1.StrictEquals('], [/(.+)->Set\(/g, '$1.Set('], [/Nan::Callback/g, 'Napi::FunctionReference'], [/Nan::Persistent/g, 'Napi::ObjectReference'], [/Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target/g, 'Napi::Env& env, Napi::Object& target'], [/(\w+)\*\s+(\w+)\s*=\s*Nan::ObjectWrap::Unwrap<\w+>\(info\.This\(\)\);/g, '$1* $2 = this;'], [/Nan::ObjectWrap::Unwrap<(\w+)>\((.*)\);/g, '$2.Unwrap<$1>();'], [/Nan::NAN_METHOD_RETURN_TYPE/g, 'void'], [/NAN_INLINE/g, 'inline'], [/Nan::NAN_METHOD_ARGS_TYPE/g, 'const Napi::CallbackInfo&'], [/NAN_METHOD\(([\w\d:]+?)\)/g, 'Napi::Value $1(const Napi::CallbackInfo& info)'], [/static\s*NAN_GETTER\(([\w\d:]+?)\)/g, 'Napi::Value $1(const Napi::CallbackInfo& info)'], [/NAN_GETTER\(([\w\d:]+?)\)/g, 'Napi::Value $1(const Napi::CallbackInfo& info)'], [/static\s*NAN_SETTER\(([\w\d:]+?)\)/g, 'void $1(const Napi::CallbackInfo& info, const Napi::Value& value)'], [/NAN_SETTER\(([\w\d:]+?)\)/g, 'void $1(const Napi::CallbackInfo& info, const Napi::Value& value)'], [/void Init\((v8::)*Local<(v8::)*Object> exports\)/g, 'Napi::Object Init(Napi::Env env, Napi::Object exports)'], [/NAN_MODULE_INIT\(([\w\d:]+?)\);/g, 'Napi::Object $1(Napi::Env env, Napi::Object exports);'], [/NAN_MODULE_INIT\(([\w\d:]+?)\)/g, 'Napi::Object $1(Napi::Env env, Napi::Object exports)'], [/::(Init(?:ialize)?)\(target\)/g, '::$1(env, target, module)'], [/constructor_template/g, 'constructor'], [/Nan::FunctionCallbackInfo<(v8::)?Value>[ ]*& [ ]*info\)[ ]*{\n*([ ]*)/gm, 'Napi::CallbackInfo& info) {\n$2Napi::Env env = info.Env();\n$2'], [/Nan::FunctionCallbackInfo<(v8::)*Value>\s*&\s*info\);/g, 'Napi::CallbackInfo& info);'], [/Nan::FunctionCallbackInfo<(v8::)*Value>\s*&/g, 'Napi::CallbackInfo&'], [/Buffer::HasInstance\(([^)]+)\)/g, '$1.IsBuffer()'], [/info\[(\d+)\]->/g, 'info[$1].'], [/info\[([\w\d]+)\]->/g, 'info[$1].'], [/info\.This\(\)->/g, 'info.This().'], [/->Is(Object|String|Int32|Number)\(\)/g, '.Is$1()'], [/info.GetReturnValue\(\).SetUndefined\(\)/g, 'return env.Undefined()'], [/info\.GetReturnValue\(\)\.Set\(((\n|.)+?)\);/g, 'return $1;'], // ex. Local to Napi::Value [/v8::Local/g, 'Napi::$1'], [/Local<(Value|Boolean|String|Number|Object|Array|Symbol|External|Function)>/g, 'Napi::$1'], // Declare an env in helper functions that take a Napi::Value [/(\w+)\(Napi::Value (\w+)(,\s*[^()]+)?\)\s*{\n*([ ]*)/gm, '$1(Napi::Value $2$3) {\n$4Napi::Env env = $2.Env();\n$4'], // delete #include and/or [/#include +(<|")(?:node|nan).h("|>)/g, '#include $1napi.h$2\n#include $1uv.h$2'], // NODE_MODULE to NODE_API_MODULE [/NODE_MODULE/g, 'NODE_API_MODULE'], [/Nan::/g, 'Napi::'], [/nan.h/g, 'napi.h'], // delete .FromJust() [/\.FromJust\(\)/g, ''], // delete .ToLocalCheck() [/\.ToLocalChecked\(\)/g, ''], [/^.*->SetInternalFieldCount\(.*$/gm, ''], // replace using node; and/or using v8; to using Napi; [/using (node|v8);/g, 'using Napi;'], [/using namespace (node|Nan|v8);/g, 'using namespace Napi;'], // delete using v8::Local; [/using v8::Local;\n/g, ''], // replace using v8::XXX; with using Napi::XXX [/using v8::([A-Za-z]+);/g, 'using Napi::$1;'] ]; const paths = listFiles(dir); paths.forEach(function (dirEntry) { const filename = dirEntry.split('\\').pop().split('/').pop(); // Check whether the file is a source file or a config file // then execute function accordingly const sourcePattern = /.+\.h|.+\.cc|.+\.cpp/; if (sourcePattern.test(filename)) { convertFile(dirEntry, SourceFileOperations); } else if (ConfigFileOperations[filename] != null) { convertFile(dirEntry, ConfigFileOperations[filename]); } }); function listFiles (dir, filelist) { const files = fs.readdirSync(dir); filelist = filelist || []; files.forEach(function (file) { if (file === 'node_modules') { return; } if (fs.statSync(path.join(dir, file)).isDirectory()) { filelist = listFiles(path.join(dir, file), filelist); } else { filelist.push(path.join(dir, file)); } }); return filelist; } function convert (content, operations) { for (let i = 0; i < operations.length; i++) { const operation = operations[i]; content = content.replace(operation[0], operation[1]); } return content; } function convertFile (fileName, operations) { fs.readFile(fileName, 'utf-8', function (err, file) { if (err) throw err; file = convert(file, operations); fs.writeFile(fileName, file, function (err) { if (err) throw err; }); }); } let path = require('path'); let util = require('util'); let os = require('os'); let crypto = require('crypto'); let fs = require('fs'); let zlib = require('zlib'); let EventEmitter = require('events'); let electron = require('electron'); electron.remote = require('@electron/remote/main'); let {app, protocol, net, remote} = electron; remote.initialize(); protocol.registerSchemesAsPrivileged([ {scheme: 'app', privileges: {standard: true, secure: true, supportFetchAPI: true, stream: true, codeCache: true}} ]); let updateEvents = new EventEmitter(); let APP_PATH = (() => { let path = app.getAppPath(); let asar_index = path.indexOf('app.asar'); if (asar_index >= 0) { return path.slice(0, asar_index); } return path; })(); let currentBaseVersion = app.getVersion(); let currentPackageVersion = currentBaseVersion; let dataPath = app.getPath('userData'); function pad(number) { if (number < 10) { return '0' + number; } return number; } function stamp() { let d = new Date(); return d.getUTCFullYear() + '-' + pad(d.getUTCMonth() + 1) + '-' + pad(d.getUTCDate()) + ' ' + pad(d.getUTCHours()) + ':' + pad(d.getUTCMinutes()) + ':' + pad(d.getUTCSeconds()); } function logger(logfile) { let fileout = fs.openSync(logfile, 'a'); let stdout = process.stdout; stdout.on('error', function(e) { // `write` failed. Do nothing... }); let fn = function () { let data = stamp() + ' ' + util.format.apply(null, arguments) + os.EOL; try { fs.writeSync(fileout, data); stdout.write(data); } catch (e) { // Failed to write to log } }; fn.end = function () { fs.closeSync(fileout); }; return fn; } let log = logger(path.join(dataPath, 'obsidian.log')); let idFile = path.join(dataPath, 'id'); let id; try { if (fs.existsSync(idFile)) { id = fs.readFileSync(idFile, 'utf8'); } } catch (e) { } try { if (!id || id.length < 16) { id = crypto.randomBytes(16).toString('hex'); fs.writeFileSync(idFile, id, 'utf8'); } } catch (e) { } let insider = false; let disable = false; updateEvents.on('insider', (arg) => { insider = arg; }); updateEvents.on('disable', (arg) => { disable = arg; }); // Only start the updater when electron is ready, because the net module requires that. let updatePromise = app.whenReady(); let queueUpdate = (manual) => { let fn = () => update(manual); updatePromise = updatePromise.then(fn, fn); }; // Used for verifying signatures of downloaded asar files const SIGNATURE_CERT = '-----BEGIN CERTIFICATE-----\n' + 'MIIDjzCCAnegAwIBAgIJAOFHLJ2gTCBzMA0GCSqGSIb3DQEBCwUAMF4xCzAJBgNV\n' + 'BAYTAlVTMRMwEQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQKDAhEeW5hbGlzdDER\n' + 'MA8GA1UECwwIRHluYWxpc3QxFDASBgNVBAMMC2R5bmFsaXN0LmlvMB4XDTE2MDUx\n' + 'NjAyMTA1NFoXDTQwMDUxMDAyMTA1NFowXjELMAkGA1UEBhMCVVMxEzARBgNVBAgM\n' + 'ClNvbWUtU3RhdGUxETAPBgNVBAoMCER5bmFsaXN0MREwDwYDVQQLDAhEeW5hbGlz\n' + 'dDEUMBIGA1UEAwwLZHluYWxpc3QuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\n' + 'ggEKAoIBAQDcodSNp30B0oE+2vRUdr//SGfbDow+67OtGuRYQSjn86bn55fQXhMJ\n' + 'b5xgZ0natiCriCyllLWgPf+4PnxGRSJZGbm38QSArb0MWR8/yXA+q+7nZisIsN2d\n' + 'Xih8B3APImxJ4A50nsK/C+fl7nYdo04iz3oerP0UhLDrsLbL+9rdmshjB1boLPf6\n' + 'QpAAC57OTPQpFBd2hFoS6xAnIb708SHOndsrWDIFEFVCPDYcme3WF5jznuT05OFG\n' + 'MIX8SZe2jXpg2Vco/1oKRPC7mYFN5B0JTZ7mOH48vB/zPNIsVz8KHh3P9Ru2fC2r\n' + '3nPDXFGKzcUZneJmXh4LIUVqwdEPw7hvAgMBAAGjUDBOMB0GA1UdDgQWBBTF2xMx\n' + '8xVDZ2wteJPsHUe0OCu18TAfBgNVHSMEGDAWgBTF2xMx8xVDZ2wteJPsHUe0OCu1\n' + '8TAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB6rgBF+DvDHifP+U6Z\n' + 'FqJ4mX1nalEXEPI1jvRZaOheKpkOEBbhkCAosbBEmYxfj8xay1GGgB9nkJk2dodR\n' + 'sGVhrZz+CwGR+hSEfYDQwMBvmzm3OcETfEtvwEAU1P93prbxul2oSWP48AVDDYKe\n' + 'pxTZvW/yZcnoHI9XzhLNMYIEvOs+wKWAOF0+BjsIukQouaXs6gklul2J99IqpdPh\n' + 'w2l4l7mkPx8htCbTE47GTraHt2i2mwyBZSKbfqzi73Fj5SFRtZlWJDNPKoWxcFg2\n' + '91B7IHumd5jwAUdVJit3K5Tgt/q4OzwokcDZcrh5lJg0+Kstsz4RDWDbfzTNJuKn\n' + 'oueR\n' + '-----END CERTIFICATE-----'; async function update(manual) { if (!manual && disable) { return; } updateEvents.emit('check-start'); try { // Hopefully this can help us catch any updater failures // Or any old buggy versions that may still be operating in the wild. // Don't wait on the async result httpGetBinary('https://releases.obsidian.md/desktop?id=' + id + '&v=' + currentPackageVersion + '&p=' + process.platform).then(() => null, () => null); } catch (e) { } try { await runUpdate(); } catch (e) { log('Failed to perform update'); console.error(e); } updateEvents.emit('check-end'); } async function runUpdate() { let updateJson = null; try { log('Checking for update using Github'); updateJson = JSON.parse(await httpGet('https://raw.githubusercontent.com/obsidianmd/obsidian-releases/master/desktop-releases.json')); log('Success.'); } catch (e) { log(`Failed to check for update using Github (${e.message})`); console.error(e); } if (updateJson) { try { await downloadUpdate(updateJson); return; } catch (e) { if (e.code !== 'ETIMEDOUT') { throw e; } log('Failed to download update from Github'); } } try { log('Checking for update using obsidian.md'); updateJson = JSON.parse(await httpGet('https://releases.obsidian.md/desktop-releases.json')); log('Success.'); } catch (e) { log(`Failed to check for update using obsidian.md (${e.message})`); console.error(e); } if (updateJson) { await downloadUpdate(updateJson); } } async function downloadUpdate(updateJson) { let usingInsider = false; if (insider && updateJson.beta) { updateJson = updateJson.beta; usingInsider = true; } let {minimumVersion, latestVersion, downloadUrl, hash, signature} = updateJson; if (!minimumVersion || !latestVersion || !downloadUrl || !hash || !signature) { log('Update failed: info incomplete', updateJson); return; } log('Latest version is ' + latestVersion + (usingInsider ? ' (insider)' : '')); // If minimum version is higher than current electron version, we will require a manual full reinstall if (isV2MoreRecent(app.getVersion(), minimumVersion)) { updateEvents.emit('update-manual-required'); log('Update failed: minimum version mismatch. App must be manually updated'); return; } // A new version is posted, go get it! if (isV2MoreRecent(currentPackageVersion, latestVersion)) { // Download file only if it's not already here let downloadPath = path.join(dataPath, 'obsidian-' + latestVersion + '.asar'); if (!fs.existsSync(downloadPath)) { log('Downloading update from', downloadUrl); // Download to a buffer so we can check its signature let compressedBuffer = await httpGetBinary(downloadUrl); log('Verifying hash & signature. Size=', compressedBuffer.byteLength); let newHash = crypto.createHash('SHA256') .update(compressedBuffer) .digest('base64') let verifiedHash = hash === newHash; let verifiedSignature = crypto.createVerify('RSA-SHA256') .update(compressedBuffer) .verify(SIGNATURE_CERT, signature, 'base64'); if (verifiedHash && verifiedSignature) { log('Saving file'); let tempDownloadPath = path.join(dataPath, 'obsidian.asar.tmp'); let decompressedBuffer = await new Promise((resolve, reject) => { zlib.gunzip(compressedBuffer, {}, (err, data) => { if (err) { return reject(err); } resolve(data); }); }); await fs.promises.writeFile(tempDownloadPath, decompressedBuffer); await fs.promises.rename(tempDownloadPath, downloadPath); log('Update complete.'); updateEvents.emit('update-downloaded'); } else { if (!verifiedHash) { log('Hash check failed!', newHash); } if (!verifiedSignature) { log('Signature check failed!'); } } } else { log('An update is already downloaded.'); } } else { log('App is up to date.'); } // Clean up old updates now that we have the latest version let files = fs.readdirSync(dataPath, {withFileTypes: true}); for (let file of files) { let filename = file.name; // Delete temporary file if (filename === 'obsidian.asar.tmp') { await fs.promises.unlink(path.join(dataPath, filename)); } // Delete all versions that aren't the latest version. if (file.isFile() && filename.startsWith('obsidian-') && filename.endsWith('.asar')) { let newVersion = extractVersion(filename); // Make an exception for the current running version. if (newVersion !== currentPackageVersion && newVersion !== latestVersion) { await fs.promises.unlink(path.join(dataPath, filename)); } } } } function loadApp(asarPath) { // Execute asar content let main = path.join(asarPath, 'main.js'); let fn; try { fn = require(main); } catch (e) { return false; } if (fn) { fn(asarPath, updateEvents); return true; } return false; } function extractVersion(filename) { if (filename.startsWith('obsidian-') && filename.endsWith('.asar')) { return filename.substring('obsidian-'.length, filename.length - '.asar'.length); } return null; } function parseVersion(string) { if (!string) { return null; } let valid = true; let parts = string.split('.').map((part) => { part = parseInt(part); if (isNaN(part)) { valid = false; } return part; }); if (valid) { return parts; } return null; } function isMoreRecent(version1, version2) { let length = Math.min(version1.length, version2.length); for (let i = 0; i < length; i++) { if (version1[i] < version2[i]) { return true; } if (version1[i] > version2[i]) { return false; } } if (version1.length < version2.length) { return true; } if (version1.length > version2.length) { return false; } return false; } function isV2MoreRecent(v1, v2) { let version1 = parseVersion(v1); let version2 = parseVersion(v2); if (!version2) { return false; } if (!version1) { return true; } return isMoreRecent(version1, version2); } function httpGet(url) { return httpGetBinary(url).then(buffer => buffer.toString('utf8')); } function httpGetBinary(url) { return new Promise((resolve, reject) => { let request = net.request({ method: 'GET', url: url, redirect: 'follow', }); request.setHeader('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36'); request.on('login', (authInfo, callback) => callback()); request.on('error', reject); request.on('response', (res) => { let data = []; res.on('data', (chunk) => data.push(chunk)); res.on('end', () => resolve(Buffer.concat(data))); }) request.end(); }); } // Source: https://github.com/electron/electron/blob/19954126e08c67022b89a886cadb10471ac853ae/lib/browser/init.ts#L20 process.on('uncaughtException', function (error) { // Don't emit errors for updater if (error.message && error.message.indexOf('net::ERR') !== -1) { return; } console.error('Uncaught Exception', error); // Show error in GUI. // We can't import { dialog } at the top of this file as this file is // responsible for setting up the require hook for the "electron" module // so we import it inside the handler down here import('electron') .then(({dialog}) => { const stack = error.stack ? error.stack : `${error.name}: ${error.message}`; const message = 'Uncaught Exception:\n' + stack; dialog.showErrorBox('A JavaScript error occurred in the main process', message); }); }); // Actual startup routine setInterval(queueUpdate, 60 * 60 * 1000); let asarPath = path.join(APP_PATH, 'obsidian.asar'); let updatedAsarPath = ''; let updatedAsarVersion = ''; // Check if we have an updated asar to replace the built-in one try { let candidateFile = ''; let version = ''; let stat = fs.statSync(dataPath); if (stat.isDirectory()) { let files = fs.readdirSync(dataPath, {withFileTypes: true}); for (let file of files) { let filename = file.name; if (file.isFile() && filename.startsWith('obsidian-') && filename.endsWith('.asar')) { let newVersion = extractVersion(filename); if (isV2MoreRecent(version, newVersion)) { candidateFile = filename; version = newVersion; } } } } // We found an updated asar if (isV2MoreRecent(app.getVersion(), version)) { updatedAsarPath = path.join(dataPath, candidateFile); updatedAsarVersion = version; } } catch (e) { if (e.code !== 'ENOENT') { console.error(e); } } let success = false; if (updatedAsarPath) { log('Loading updated app package', updatedAsarPath); success = loadApp(updatedAsarPath); currentPackageVersion = updatedAsarVersion; } if (!success) { log('Loading main app package', asarPath); success = loadApp(asarPath); } if (!success) { log('Failed to load both app packages.'); } queueUpdate(); updateEvents.on('check', () => queueUpdate(true)); { "name": "obsidian", "description": "Obsidian", "version": "1.10.6", "homepage": "https://obsidian.md", "license": "UNLICENSED", "author": "Obsidian", "private": true, "main": "main.js", "dependencies": { "@electron/remote": "2.1.3", "btime": "../btime", "get-fonts": "../get-fonts" } }