Compare commits
275
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9db1cb9ca1 | ||
|
|
1bcbce2d5b | ||
|
|
22d6404799 | ||
|
|
932e643dc9 | ||
|
|
1cc678fbef | ||
|
|
122fd03a29 | ||
|
|
a3b153eb99 | ||
|
|
b36d8b167f | ||
|
|
cae6d5072c | ||
|
|
8dce40354a | ||
|
|
19b5b2b049 | ||
|
|
64ee722124 | ||
|
|
77282e3b7b | ||
|
|
927b72c91a | ||
|
|
10b67def74 | ||
|
|
3f1f0e7234 | ||
|
|
c7160d05fd | ||
|
|
05577f7559 | ||
|
|
e26385f591 | ||
|
|
33583e751f | ||
|
|
742b40c86c | ||
|
|
bf602f758b | ||
|
|
34b07378fd | ||
|
|
6622220415 | ||
|
|
ca0599b180 | ||
|
|
d055203559 | ||
|
|
65b43ebe78 | ||
|
|
9aa3481f62 | ||
|
|
e087521a36 | ||
|
|
f43221c3bb | ||
|
|
e71f5a0471 | ||
|
|
73c17e51dc | ||
|
|
e6b5d6af49 | ||
|
|
9833c61f78 | ||
|
|
a1ee770794 | ||
|
|
270087fc62 | ||
|
|
bd0ae3fbe1 | ||
|
|
241d97aed0 | ||
|
|
b25ecb08bc | ||
|
|
c45373aa55 | ||
|
|
fe673c21b2 | ||
|
|
77420e9873 | ||
|
|
e4789d9c40 | ||
|
|
9f0937b5c3 | ||
|
|
bb46d45d56 | ||
|
|
ad18c12326 | ||
|
|
3a5c4f2164 | ||
|
|
b683c981b0 | ||
|
|
9b614421b6 | ||
|
|
26659db833 | ||
|
|
6b5a47b017 | ||
|
|
3ec4fc9f0b | ||
|
|
5d2a99d40e | ||
|
|
1d06905fc5 | ||
|
|
3f98441918 | ||
|
|
f8e00859cd | ||
|
|
933c9cb5e3 | ||
|
|
11b3a52287 | ||
|
|
07f7a75759 | ||
|
|
644c75094e | ||
|
|
a565cb7b2a | ||
|
|
6db0f7c60b | ||
|
|
bfe0b0e99e | ||
|
|
13bdf30055 | ||
|
|
f18c7a2210 | ||
|
|
123a7de64a | ||
|
|
e0e1436930 | ||
|
|
b96e6c8149 | ||
|
|
f6402ac638 | ||
|
|
1e55d0200b | ||
|
|
1725dfd1ea | ||
|
|
a8cc0ccc86 | ||
|
|
7dd868aac7 | ||
|
|
220e5ce007 | ||
|
|
80f6ea4502 | ||
|
|
eb820aaf6a | ||
|
|
a754c162d9 | ||
|
|
de18ceb887 | ||
|
|
51188173bb | ||
|
|
d0797ad5d8 | ||
|
|
9b37bd532e | ||
|
|
333352edcf | ||
|
|
05783773af | ||
|
|
59ee20ee7a | ||
|
|
87f2c1b062 | ||
|
|
b85cefea56 | ||
|
|
2e28f863f3 | ||
|
|
ddaa4d1741 | ||
|
|
732bd1520a | ||
|
|
1d67946f3f | ||
|
|
fab6d7e662 | ||
|
|
6e20578a81 | ||
|
|
dc5282c32e | ||
|
|
812abae6e5 | ||
|
|
b3b512d027 | ||
|
|
ed296c4c7f | ||
|
|
595bb47249 | ||
|
|
daad1a4fa8 | ||
|
|
daef1ac198 | ||
|
|
3b9d88b252 | ||
|
|
809f8229b4 | ||
|
|
ffb1ee2523 | ||
|
|
0903718314 | ||
|
|
e0cb883ae4 | ||
|
|
17449dd9d5 | ||
|
|
7963631a1d | ||
|
|
77d88f4b34 | ||
|
|
c5e3ab1162 | ||
|
|
75041c1db9 | ||
|
|
558a4c0782 | ||
|
|
995c3b9c8e | ||
|
|
0a64bca9ce | ||
|
|
b7b12b6cbb | ||
|
|
5684ca8671 | ||
|
|
3c81360dc2 | ||
|
|
fc50ebdf90 | ||
|
|
4d6e8db5ff | ||
|
|
938ffac57e | ||
|
|
50aaffb101 | ||
|
|
4f9b449c6e | ||
|
|
c1a36a3f4b | ||
|
|
4ca6e2a680 | ||
|
|
11db805c34 | ||
|
|
f1f700ab69 | ||
|
|
9eeb8ddb80 | ||
|
|
95de26a6d0 | ||
|
|
88c2e28dbc | ||
|
|
30b7a9e4d4 | ||
|
|
37542065d6 | ||
|
|
c9608f7760 | ||
|
|
6ba54b0e77 | ||
|
|
f9bb3105c8 | ||
|
|
4b5baa9b5f | ||
|
|
6dedd87260 | ||
|
|
91317222fb | ||
|
|
494953a8cc | ||
|
|
ae4d8be50c | ||
|
|
d34790e05c | ||
|
|
b77e15c6d6 | ||
|
|
54195d5811 | ||
|
|
9c7907f91a | ||
|
|
0601b8def6 | ||
|
|
841261f199 | ||
|
|
7124b6c4ce | ||
|
|
ad7beec559 | ||
|
|
bd4fc51392 | ||
|
|
54d994a879 | ||
|
|
891a085f2b | ||
|
|
e7ccd2e72d | ||
|
|
459a39e65d | ||
|
|
36eb0fca8b | ||
|
|
53b9e22f26 | ||
|
|
8ff3f5e351 | ||
|
|
e6a2c443cb | ||
|
|
4f25ab162d | ||
|
|
0520d9f187 | ||
|
|
cc3c21b7e3 | ||
|
|
ae9560124e | ||
|
|
085f4ec37e | ||
|
|
476c3a9cc9 | ||
|
|
1bc5ac8768 | ||
|
|
4bdff11b87 | ||
|
|
b32dde3c91 | ||
|
|
b9a566350b | ||
|
|
ce9ce674ea | ||
|
|
b8641779bd | ||
|
|
5e620d784e | ||
|
|
013bcfaffb | ||
|
|
2504c96927 | ||
|
|
e2f4d1481e | ||
|
|
ea3d68573b | ||
|
|
c6cb594ef1 | ||
|
|
948123bc7a | ||
|
|
68f5b26af9 | ||
|
|
5d493630b1 | ||
|
|
f4db6faa80 | ||
|
|
e340f7f5ad | ||
|
|
b4d923b74c | ||
|
|
965f334d59 | ||
|
|
54463b78d2 | ||
|
|
600aec1f50 | ||
|
|
bcfbe81979 | ||
|
|
31e8851bcc | ||
|
|
f464767ee7 | ||
|
|
22eb58b3de | ||
|
|
0daee20545 | ||
|
|
d98073dc33 | ||
|
|
fb199e7dea | ||
|
|
6cea0d97c2 | ||
|
|
058dd2e01e | ||
|
|
975d122491 | ||
|
|
eef4c66374 | ||
|
|
5ce4fbd74d | ||
|
|
f4d8cff26b | ||
|
|
46b6b3c151 | ||
|
|
796b217dec | ||
|
|
1ccc35f464 | ||
|
|
2ed6f15fe1 | ||
|
|
575040be65 | ||
|
|
206959adbd | ||
|
|
dd1c97b6af | ||
|
|
85463dcff0 | ||
|
|
7ff816417e | ||
|
|
3fa2f3898a | ||
|
|
514ce9eec2 | ||
|
|
5bc357b4b3 | ||
|
|
56ddc3c24e | ||
|
|
e407b2a307 | ||
|
|
1ac194ed1d | ||
|
|
62bf5d4cc9 | ||
|
|
27093064a9 | ||
|
|
860a3018a7 | ||
|
|
eb3992dbdb | ||
|
|
8c5f9cc1d3 | ||
|
|
ed78703090 | ||
|
|
29b61bcee9 | ||
|
|
0ff49845a7 | ||
|
|
c17366b2b1 | ||
|
|
5da267fb31 | ||
|
|
dffa3345d0 | ||
|
|
a23c767fb0 | ||
|
|
5d6ec51302 | ||
|
|
585f277bf5 | ||
|
|
aae5f431cb | ||
|
|
4eb84e5c32 | ||
|
|
70b11ec7ad | ||
|
|
f2944e0bae | ||
|
|
fdec33d086 | ||
|
|
0702074469 | ||
|
|
6d5cbf6974 | ||
|
|
5429a3a4d4 | ||
|
|
c124076279 | ||
|
|
0d1d811d2c | ||
|
|
8c04831339 | ||
|
|
4a8b2690aa | ||
|
|
e2bc716ea7 | ||
|
|
6c5987f9af | ||
|
|
1e79880c72 | ||
|
|
f62d891c63 | ||
|
|
930cb8853c | ||
|
|
22104ff64e | ||
|
|
17564441b5 | ||
|
|
a9140a4403 | ||
|
|
ce47ca0a8a | ||
|
|
852fee170e | ||
|
|
8463e99d13 | ||
|
|
3c016b73ee | ||
|
|
8d4173b0be | ||
|
|
502f043804 | ||
|
|
f8571fb175 | ||
|
|
7209aa8880 | ||
|
|
1a4a1217ae | ||
|
|
139c360feb | ||
|
|
be1afbdb5c | ||
|
|
8a7e8c8d7d | ||
|
|
931f79dacb | ||
|
|
261e6ee78e | ||
|
|
9eaddcdafb | ||
|
|
b8ede1a3e7 | ||
|
|
bbaacd5d28 | ||
|
|
60ed5fdfe2 | ||
|
|
512f88c63e | ||
|
|
50c01b2cfb | ||
|
|
27d3f889b5 | ||
|
|
140a9918c0 | ||
|
|
fc6a30b684 | ||
|
|
deb0ae8b8e | ||
|
|
1425479569 | ||
|
|
09923651d8 | ||
|
|
7ca5b85051 | ||
|
|
a34faf8a26 | ||
|
|
7708159e92 | ||
|
|
7851d48e82 | ||
|
|
f320b4fa9b | ||
|
|
b3ea81f4e8 |
@@ -1,2 +1,4 @@
|
||||
/hscript.swf
|
||||
/release.zip
|
||||
dump/*
|
||||
tests/bin/*
|
||||
+18
-24
@@ -4,10 +4,11 @@ os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
osx_image: xcode6.4
|
||||
dist: bionic
|
||||
|
||||
haxe:
|
||||
- "3.2.1"
|
||||
- "3.4.7"
|
||||
- "4.0.3"
|
||||
- development
|
||||
|
||||
addons:
|
||||
@@ -19,44 +20,34 @@ addons:
|
||||
# Python
|
||||
- python3
|
||||
# PHP
|
||||
- php5-cli
|
||||
- php-cli
|
||||
- php-mbstring
|
||||
# C#
|
||||
- mono-devel
|
||||
- mono-mcs
|
||||
# Flash
|
||||
- libcurl3:i386
|
||||
- libglib2.0-0:i386
|
||||
- libx11-6:i386
|
||||
- libxext6:i386
|
||||
- libxt6:i386
|
||||
- libxcursor1:i386
|
||||
- libnss3:i386
|
||||
- libgtk2.0-0:i386
|
||||
|
||||
install:
|
||||
# os-specific config
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
|
||||
export DISPLAY=:99.0;
|
||||
export AUDIODEV=null;
|
||||
elif [ "${TRAVIS_OS_NAME}" = "osx" ]; then
|
||||
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then
|
||||
brew update;
|
||||
brew install mono;
|
||||
brew install python3;
|
||||
brew install php;
|
||||
fi
|
||||
# Download and setup a standalone flash player debugger
|
||||
- haxe flash/install.hxml
|
||||
# Install haxelibs
|
||||
- if [ "${TRAVIS_HAXE_VERSION}" = "development" ]; then
|
||||
haxelib git hxcpp https://github.com/HaxeFoundation/hxcpp.git;
|
||||
else
|
||||
haxelib install hxcpp;
|
||||
fi
|
||||
- if [ "${TRAVIS_HAXE_VERSION}" = "development" ] || [ "${TRAVIS_HAXE_VERSION}" = "3.2.1" ]; then
|
||||
pushd $(haxelib path hxcpp | head -1);
|
||||
pushd tools/hxcpp; haxe compile.hxml; popd;
|
||||
pushd project; neko build.n; popd;
|
||||
popd;
|
||||
else
|
||||
haxelib install hxcpp;
|
||||
fi
|
||||
- haxelib install hxjava
|
||||
- haxelib install hxcs
|
||||
- haxelib install hx3compat
|
||||
- haxelib dev hscript .
|
||||
- haxelib list
|
||||
|
||||
@@ -65,11 +56,14 @@ script:
|
||||
haxe build.hxml &&
|
||||
popd
|
||||
- haxe bin/build-interp.hxml
|
||||
- haxe bin/build-interp.hxml -D hscriptPos
|
||||
- haxe bin/build-neko.hxml && neko bin/Test.n
|
||||
- haxe bin/build-js.hxml && node bin/Test.js
|
||||
- haxe bin/build-java.hxml && java -jar bin/Test.jar
|
||||
- haxe bin/build-cs.hxml && mono bin/bin/Test.exe
|
||||
# Only test C# for Haxe 4+
|
||||
- if [[ $(haxe -version 2&>1 | head -c 1) > 3 ]]; then
|
||||
haxe bin/build-cs.hxml && mono bin/bin/Test.exe;
|
||||
fi
|
||||
- haxe bin/build-cpp.hxml && ./bin/Test
|
||||
- haxe bin/build-flash.hxml -D fdb && haxe flash/run.hxml bin/Test.swf
|
||||
- haxe bin/build-php.hxml && php bin/index.php
|
||||
- haxe bin/build-php.hxml -D php7 && php bin/index.php
|
||||
- haxe bin/build-python.hxml && python3 bin/Test.py
|
||||
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
// Utilisez IntelliSense pour en savoir plus sur les attributs possibles.
|
||||
// Pointez pour afficher la description des attributs existants.
|
||||
// Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "HashLink (launch)",
|
||||
"request": "launch",
|
||||
"type": "hl",
|
||||
"hxml": "hscript.hxml",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build",
|
||||
"type": "hxml",
|
||||
"file": "hscript.hxml",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (C)2008-2017 Haxe Foundation
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
hscript
|
||||
hscript-improved
|
||||
=======
|
||||
|
||||
[](https://travis-ci.org/HaxeFoundation/hscript)
|
||||
[](https://ci.appveyor.com/project/HaxeFoundation/hscript)
|
||||
How to install
|
||||
```
|
||||
haxelib git hscript-improved https://github.com/CodenameCrew/hscript-improved.git
|
||||
```
|
||||
|
||||
To enable custom classes support you have to do this in project.xml
|
||||
```xml
|
||||
<define name="CUSTOM_CLASSES" />
|
||||
```
|
||||
Warning: custom classes are sometimes broken, would like help to fix them. You can only override functions from the current class, not from the extended part, like you cant override update in FlxText because FlxText doesnt have a update function overriden
|
||||
|
||||
-----------
|
||||
|
||||
Parse and evalutate Haxe expressions.
|
||||
|
||||
@@ -64,26 +74,40 @@ You can use `parser.allowJSON` to allow JSON data.
|
||||
|
||||
You can use `parser.allowTypes` to parse types for local vars, exceptions, function args and return types. Types are ignored by the interpreter.
|
||||
|
||||
You can use `parser.allowMetadata` to parse metadata before expressions on in anonymous types. Metadata are ignored by the interpreter.
|
||||
|
||||
You can use `new hscript.Macro(pos).convert(ast)` to convert an hscript AST to a Haxe macros one.
|
||||
|
||||
You can use `hscript.Checker` in order to type check and even get completion, using `haxe -xml` output for type information.
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
Compared to Haxe, limitations are :
|
||||
|
||||
- no type declarations (classes, enums, typedefs) : only expressions
|
||||
- `switch` construct is supported but not pattern matching (no variable capture, we use strict equality to compare `case` values and `switch` value)
|
||||
- only one variable declaration is allowed in `var`
|
||||
- the parser supports optional types for `var` and `function` if `allowTypes` is set, but the interpreter ignores them
|
||||
- you can enable per-expression position tracking by compiling with `-D hscriptPos`
|
||||
- you can parse some type declarations (import, class, typedef, etc.) with parseModule
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
In order to install Haxe Script, use `haxelib install hscript` and compile your program with `-lib hscript`.
|
||||
|
||||
There are only three files in hscript :
|
||||
These are the main required files in hscript :
|
||||
|
||||
- `hscript.Expr` : contains enums declarations
|
||||
- `hscript.Parser` : a small parser that turns a string into an expression structure (AST)
|
||||
- `hscript.Interp` : a small interpreter that execute the AST and returns the latest evaluated value
|
||||
|
||||
Some other optional files :
|
||||
|
||||
- `hscript.Async` : converts Expr into asynchronous version
|
||||
- `hscript.Bytes` : Expr serializer/unserializer
|
||||
- `hscript.Checker` : type checking and completion for hscript Expr
|
||||
- `hscript.Macro` : convert Haxe macro into hscript Expr
|
||||
- `hscript.Printer` : convert hscript Expr to String
|
||||
- `hscript.Tools` : utility functions (map/iter)
|
||||
|
||||
|
||||
+35
-15
@@ -1,10 +1,14 @@
|
||||
import haxe.ds.EnumValueMap;
|
||||
import haxe.ds.Option;
|
||||
import hscript.Macro;
|
||||
import hscript.Tools;
|
||||
import hscript.Async;
|
||||
import hscript.Printer;
|
||||
import hscript.Checker;
|
||||
import haxe.unit.*;
|
||||
|
||||
class Test extends TestCase {
|
||||
function assertScript(x,v:Dynamic,?vars : Dynamic,allowTypes=false) {
|
||||
class TestHScript extends TestCase {
|
||||
function assertScript(x,v:Dynamic,?vars : Dynamic, allowTypes=false, ?pos:haxe.PosInfos) {
|
||||
var p = new hscript.Parser();
|
||||
p.allowTypes = allowTypes;
|
||||
var program = p.parseString(x);
|
||||
@@ -15,25 +19,24 @@ class Test extends TestCase {
|
||||
for( v in Reflect.fields(vars) )
|
||||
interp.variables.set(v,Reflect.field(vars,v));
|
||||
var ret : Dynamic = interp.execute(program);
|
||||
assertEquals(v, ret);
|
||||
assertEquals(v, ret, pos);
|
||||
}
|
||||
|
||||
function test():Void {
|
||||
assertScript("0",0);
|
||||
assertScript("0xFF", 255);
|
||||
#if !(php || python)
|
||||
#if haxe3
|
||||
assertScript("0xBFFFFFFF", 0xBFFFFFFF);
|
||||
assertScript("0x7FFFFFFF", 0x7FFFFFFF);
|
||||
#elseif !neko
|
||||
assertScript("n(0xBFFFFFFF)", 0xBFFFFFFF, { n : haxe.Int32.toNativeInt });
|
||||
assertScript("n(0x7FFFFFFF)", 0x7FFFFFFF, { n : haxe.Int32.toNativeInt } );
|
||||
#end
|
||||
#end
|
||||
assertScript("-123",-123);
|
||||
assertScript("- 123",-123);
|
||||
assertScript("1.546",1.546);
|
||||
assertScript(".545",.545);
|
||||
assertScript("1e5",100000);
|
||||
assertScript("1.2e2",120);
|
||||
assertScript("100e-2",1);
|
||||
assertScript("1.2e-1",0.12);
|
||||
assertScript("'bla'","bla");
|
||||
assertScript("null",null);
|
||||
assertScript("true",true);
|
||||
@@ -54,6 +57,7 @@ class Test extends TestCase {
|
||||
assertScript("3 * 2 // + 5 \n + 6",12);
|
||||
assertScript("3 /* 2\n */ + 5",8);
|
||||
assertScript("[55,66,77][1]",66);
|
||||
assertScript("[11,22,33,][2]", 33);
|
||||
assertScript("var a = [55]; a[0] *= 2; a[0]",110);
|
||||
assertScript("x",55,{ x : 55 });
|
||||
assertScript("var y = 33; y",33);
|
||||
@@ -77,11 +81,7 @@ class Test extends TestCase {
|
||||
assertScript("var a = [1,[2,[3,[4,null]]]]; var t = 0; while( a != null ) { t += a[0]; a = a[1]; }; t",10);
|
||||
assertScript("var a = false; do { a = true; } while (!a); a;",true);
|
||||
assertScript("var t = 0; for( x in 1...10 ) t += x; t", 45);
|
||||
#if haxe3
|
||||
assertScript("var t = 0; for( x in new IntIterator(1,10) ) t +=x; t", 45);
|
||||
#else
|
||||
assertScript("var t = 0; for( x in new IntIter(1,10) ) t +=x; t", 45);
|
||||
#end
|
||||
assertScript("var x = 1; try { var x = 66; throw 789; } catch( e : Dynamic ) e + x",790);
|
||||
assertScript("var x = 1; var f = function(x) throw x; try f(55) catch( e : Dynamic ) e + x",56);
|
||||
assertScript("var i=2; if( true ) --i; i",1);
|
||||
@@ -96,8 +96,21 @@ class Test extends TestCase {
|
||||
assertScript("var a:Array<Dynamic>=[1,2,4]; a[2]", 4, null, true);
|
||||
assertScript("/**/0", 0);
|
||||
assertScript("x=1;x*=-2", -2);
|
||||
assertScript("var f = x -> x + 1; f(3)", 4);
|
||||
assertScript("var f = () -> 55; f()", 55);
|
||||
assertScript("var f = (x) -> x + 1; f(3)", 4);
|
||||
assertScript("var f = (x:Int) -> x + 1; f(3)", 4);
|
||||
assertScript("var f = (x,y) -> x + y; f(3,1)", 4);
|
||||
assertScript("var f = (x,y:Int) -> x + y; f(3,1)", 4);
|
||||
assertScript("var f = (x:Int,y:Int) -> x + y; f(3,1)", 4);
|
||||
assertScript("var f:Int->Int->Int = (x:Int,y:Int) -> x + y; f(3,1)", 4, null, true);
|
||||
assertScript("var f:(x:Int, y:Int)->Int = (x:Int,y:Int) -> x + y; f(3,1)", 4, null, true);
|
||||
assertScript("var f:(x:Int)->(y:Int, z:Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2)", 6, null, true);
|
||||
assertScript("var f:(x:Int)->(Int, Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2)", 6, null, true);
|
||||
assertScript("var a = 10; var b = 5; a - -b", 15);
|
||||
assertScript("var a = 10; var b = 5; a - b / 2", 7.5);
|
||||
}
|
||||
|
||||
|
||||
function testMap():Void {
|
||||
var objKey = { ok:true };
|
||||
var vars = {
|
||||
@@ -110,7 +123,7 @@ class Test extends TestCase {
|
||||
stringIntMap: ["foo" => 100]
|
||||
}
|
||||
vars.enumMap.set(vars.enumKey, "ok");
|
||||
|
||||
|
||||
assertScript('stringMap["foo"]', "Foo", vars);
|
||||
assertScript('intMap[100]', "one hundred", vars);
|
||||
assertScript('objMap[objKey]', "ok", vars);
|
||||
@@ -149,9 +162,16 @@ class Test extends TestCase {
|
||||
}
|
||||
|
||||
static function main() {
|
||||
#if ((haxe_ver < 4) && php)
|
||||
// uncaught exception: The each() function is deprecated. This message will be suppressed on further calls (errno: 8192)
|
||||
// in file: /Users/travis/build/andyli/hscript/bin/lib/Type.class.php line 178
|
||||
untyped __php__("error_reporting(E_ALL ^ E_DEPRECATED);");
|
||||
#end
|
||||
|
||||
var runner = new TestRunner();
|
||||
runner.add(new Test());
|
||||
runner.add(new TestHScript());
|
||||
var succeed = runner.run();
|
||||
|
||||
#if sys
|
||||
Sys.exit(succeed ? 0 : 1);
|
||||
#elseif flash
|
||||
+7
-10
@@ -5,20 +5,19 @@ environment:
|
||||
HAXELIB_ROOT: C:\projects\haxelib
|
||||
|
||||
install:
|
||||
# http://help.appveyor.com/discussions/problems/5616-not-able-to-build-due-to-problem-in-chocolateyinstallps1
|
||||
- ps: Set-Service wuauserv -StartupType Manual
|
||||
# Install the haxe chocolatey package (https://chocolatey.org/packages/haxe)
|
||||
- cinst haxe -version 3.2.1 -y
|
||||
- cinst haxe -version 4.0.3 -y
|
||||
- RefreshEnv
|
||||
# Setup haxelib
|
||||
- mkdir "%HAXELIB_ROOT%"
|
||||
- haxelib setup "%HAXELIB_ROOT%"
|
||||
# Install test dependencies
|
||||
- cinst php -y
|
||||
# Download and setup a standalone flash player debugger
|
||||
- haxe flash/install.hxml
|
||||
# Install project dependencies
|
||||
- haxelib install hxcpp > log.txt || type log.txt && cmd /C exit 1
|
||||
- haxelib install hxjava > log.txt || type log.txt && cmd /C exit 1
|
||||
- haxelib install hxcs > log.txt || type log.txt && cmd /C exit 1
|
||||
- haxelib install hxcpp > log.txt || type log.txt && cmd /C exit 1
|
||||
- haxelib install hxjava > log.txt || type log.txt && cmd /C exit 1
|
||||
- haxelib install hxcs > log.txt || type log.txt && cmd /C exit 1
|
||||
- haxelib install hx3compat > log.txt || type log.txt && cmd /C exit 1
|
||||
- haxelib dev hscript .
|
||||
- haxelib list
|
||||
|
||||
@@ -34,6 +33,4 @@ test_script:
|
||||
- haxe bin/build-java.hxml && java -jar bin\Test.jar
|
||||
- haxe bin/build-cs.hxml && bin\bin\Test.exe
|
||||
- haxe bin/build-cpp.hxml && bin\Test.exe
|
||||
- haxe bin/build-flash.hxml -D fdb && haxe flash\run.hxml bin\Test.swf
|
||||
- haxe bin/build-php.hxml && C:\tools\php\php.exe bin\index.php
|
||||
- haxe bin/build-python.hxml && C:\Python34-x64\python.exe bin\Test.py
|
||||
+2
-1
@@ -1,2 +1,3 @@
|
||||
-main Test
|
||||
-main TestHScript
|
||||
-dce no
|
||||
-lib hx3compat
|
||||
+4
-1
@@ -1 +1,4 @@
|
||||
--macro keep('IntIterator')
|
||||
--macro keep('IntIterator')
|
||||
--macro hscript.macros.AbstractHandler.init()
|
||||
--macro hscript.macros.UsingMacro.init()
|
||||
--macro hscript.macros.ClassExtendMacro.init()
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
*.exe
|
||||
*.app
|
||||
flashplayerdebugger
|
||||
@@ -1,91 +0,0 @@
|
||||
import Sys.*;
|
||||
import sys.io.File.*;
|
||||
import haxe.*;
|
||||
import haxe.io.*;
|
||||
|
||||
class Install {
|
||||
// https://www.adobe.com/support/flashplayer/downloads.html
|
||||
static var fpDownload(default, never) = switch (systemName()) {
|
||||
case "Linux":
|
||||
"https://fpdownload.macromedia.com/pub/flashplayer/updaters/11/flashplayer_11_sa_debug.i386.tar.gz";
|
||||
case "Mac":
|
||||
"https://fpdownload.macromedia.com/pub/flashplayer/updaters/21/flashplayer_21_sa_debug.dmg";
|
||||
case "Windows":
|
||||
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/21/flashplayer_21_sa_debug.exe";
|
||||
case _:
|
||||
throw "unsupported system";
|
||||
}
|
||||
// https://helpx.adobe.com/flash-player/kb/configure-debugger-version-flash-player.html
|
||||
static var mmcfg(default, never) = switch (systemName()) {
|
||||
case "Linux":
|
||||
Path.join([getEnv("HOME"), "mm.cfg"]);
|
||||
case "Mac":
|
||||
"/Library/Application Support/Macromedia/mm.cfg";
|
||||
case "Windows":
|
||||
Path.join([getEnv("HOMEDRIVE") + getEnv("HOMEPATH"), "mm.cfg"]);
|
||||
case _:
|
||||
throw "unsupported system";
|
||||
}
|
||||
// http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7c95.html
|
||||
static var fpTrust(default, never) = switch (systemName()) {
|
||||
case "Linux":
|
||||
Path.join([getEnv("HOME"), ".macromedia/Flash_Player/#Security/FlashPlayerTrust"]);
|
||||
case "Mac":
|
||||
"/Library/Application Support/Macromedia/FlashPlayerTrust";
|
||||
case "Windows":
|
||||
Path.join([getEnv("SYSTEMROOT"), "system32", "Macromed", "Flash", "FlashPlayerTrust"]);
|
||||
case _:
|
||||
throw "unsupported system";
|
||||
}
|
||||
static function main() {
|
||||
switch (systemName()) {
|
||||
case "Linux":
|
||||
// Download and unzip flash player
|
||||
if (command("wget", [fpDownload]) != 0)
|
||||
throw "failed to download flash player";
|
||||
if (command("tar", ["-xf", Path.withoutDirectory(fpDownload), "-C", "flash"]) != 0)
|
||||
throw "failed to extract flash player";
|
||||
case "Mac":
|
||||
if (command("brew", ["tap", "caskroom/versions"]) != 0)
|
||||
throw "failed to brew tap caskroom/versions";
|
||||
if (command("brew", ["cask", "install", "flash-player-debugger"]) != 0)
|
||||
throw "failed to install flash-player-debugger";
|
||||
case "Windows":
|
||||
// Download flash player
|
||||
download(fpDownload, "flash\\flashplayer.exe");
|
||||
case _:
|
||||
throw "unsupported system";
|
||||
}
|
||||
|
||||
|
||||
// Create a configuration file so the trace log is enabled
|
||||
createDirectory(Path.directory(mmcfg));
|
||||
saveContent(mmcfg, "ErrorReportingEnable=1\nTraceOutputFileEnable=1");
|
||||
|
||||
// Add the current directory as trusted, so exit() can be used
|
||||
createDirectory(fpTrust);
|
||||
saveContent(Path.join([fpTrust, "test.cfg"]), getCwd());
|
||||
}
|
||||
static function download(url:String, saveAs:String):Void {
|
||||
var http = new Http(url);
|
||||
http.onError = function(e) {
|
||||
throw e;
|
||||
};
|
||||
http.customRequest(false, write(saveAs));
|
||||
}
|
||||
static function createDirectory(dir:String):Void {
|
||||
try {
|
||||
sys.FileSystem.createDirectory(dir);
|
||||
} catch(e:Dynamic) {
|
||||
switch (systemName()) {
|
||||
case "Mac", "Linux":
|
||||
if (command("sudo", ["mkdir", "-p", dir]) != 0)
|
||||
throw 'cannot create $dir';
|
||||
if (command("sudo", ["chmod", "a+rw", dir]) != 0)
|
||||
throw 'cannot set permission of $dir';
|
||||
case _:
|
||||
neko.Lib.rethrow(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import Sys.*;
|
||||
import sys.FileSystem.*;
|
||||
import sys.io.File.*;
|
||||
import haxe.io.*;
|
||||
|
||||
class Run {
|
||||
// https://helpx.adobe.com/flash-player/kb/configure-debugger-version-flash-player.html
|
||||
static var flashlog(default, never) = switch (systemName()) {
|
||||
case "Linux":
|
||||
Path.join([getEnv("HOME"), ".macromedia/Flash_Player/Logs/flashlog.txt"]);
|
||||
case "Mac":
|
||||
Path.join([getEnv("HOME"), "Library/Preferences/Macromedia/Flash Player/Logs/flashlog.txt"]);
|
||||
case "Windows":
|
||||
Path.join([getEnv("APPDATA"), "Macromedia", "Flash Player", "Logs", "flashlog.txt"]);
|
||||
case _:
|
||||
throw "unsupported system";
|
||||
}
|
||||
static function main() {
|
||||
var args = args();
|
||||
var swf = args[0];
|
||||
var exitCode = switch (systemName()) {
|
||||
case "Linux":
|
||||
// The flash player has some issues with unexplained crashes,
|
||||
// but if it runs about 8 times, it should succeed one of those...
|
||||
var c = -1;
|
||||
for (i in 0...8) {
|
||||
if ((c = command("xvfb-run", ["flash/flashplayerdebugger", swf])) == 0)
|
||||
break;
|
||||
println('retry... (${i+1})');
|
||||
}
|
||||
c;
|
||||
case "Mac":
|
||||
command("/Applications/Flash Player Debugger.app/Contents/MacOS/Flash Player Debugger", [fullPath(swf)]);
|
||||
case "Windows":
|
||||
command("flash\\flashplayer.exe", [fullPath(swf)]);
|
||||
case _:
|
||||
throw "unsupported platform";
|
||||
}
|
||||
if (exists(flashlog))
|
||||
println(getContent(flashlog));
|
||||
else {
|
||||
println('does not exist: $flashlog');
|
||||
var parts = Path.normalize(flashlog).split("/");
|
||||
println(parts);
|
||||
for (i in 0...parts.length-1) {
|
||||
var path = parts.splice(0, i+1).join("/");
|
||||
println('ls $path');
|
||||
command("ls", [path]);
|
||||
}
|
||||
}
|
||||
exit(exitCode);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
-cp flash
|
||||
--run Install
|
||||
@@ -1,2 +0,0 @@
|
||||
-cp flash
|
||||
--run Run
|
||||
+9
-9
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "hscript",
|
||||
"url": "https://github.com/HaxeFoundation/hscript",
|
||||
"license": "MIT",
|
||||
"description": "Haxe Script is a scripting engine for a subset of the Haxe language",
|
||||
"version": "2.0.7",
|
||||
"releasenote": "fixes for haxe 3.3, use property access by default",
|
||||
"contributors": ["ncannasse"]
|
||||
}
|
||||
{
|
||||
"name": "hscript",
|
||||
"url": "https://github.com/HaxeFoundation/hscript",
|
||||
"license": "MIT",
|
||||
"description": "Haxe Script is a scripting engine for a subset of the Haxe language",
|
||||
"version": "2.4.0",
|
||||
"releasenote": "Haxe 4.0 final + type checking/completion (hscript.Checker)",
|
||||
"contributors": ["ncannasse","HaxeFoundation"]
|
||||
}
|
||||
|
||||
+2
-1
@@ -1 +1,2 @@
|
||||
bin/build-flash.hxml
|
||||
bin/build-each.hxml
|
||||
-hl bin/Test.hl
|
||||
@@ -1,65 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<project version="2">
|
||||
<!-- Output SWF options -->
|
||||
<output>
|
||||
<movie outputType="Application" />
|
||||
<movie input="" />
|
||||
<movie path="hscript.swf" />
|
||||
<movie fps="30" />
|
||||
<movie width="800" />
|
||||
<movie height="600" />
|
||||
<movie version="9" />
|
||||
<movie minorVersion="0" />
|
||||
<movie platform="Flash Player" />
|
||||
<movie background="#FFFFFF" />
|
||||
</output>
|
||||
<!-- Other classes to be compiled into your SWF -->
|
||||
<classpaths>
|
||||
<!-- example: <class path="..." /> -->
|
||||
</classpaths>
|
||||
<!-- Build options -->
|
||||
<build>
|
||||
<option directives="" />
|
||||
<option flashStrict="True" />
|
||||
<option noInlineOnDebug="False" />
|
||||
<option mainClass="Test" />
|
||||
<option enabledebug="False" />
|
||||
<option additional="-dce no" />
|
||||
</build>
|
||||
<!-- haxelib libraries -->
|
||||
<haxelib>
|
||||
<!-- example: <library name="..." /> -->
|
||||
</haxelib>
|
||||
<!-- Class files to compile (other referenced classes will automatically be included) -->
|
||||
<compileTargets>
|
||||
<!-- example: <compile path="..." /> -->
|
||||
</compileTargets>
|
||||
<!-- Assets to embed into the output SWF -->
|
||||
<library>
|
||||
<!-- example: <asset path="..." id="..." update="..." glyphs="..." mode="..." place="..." sharepoint="..." /> -->
|
||||
</library>
|
||||
<!-- Paths to exclude from the Project Explorer tree -->
|
||||
<hiddenPaths>
|
||||
<hidden path="haxelib.xml" />
|
||||
<hidden path="hscript.hxml" />
|
||||
<hidden path="hscript.js" />
|
||||
<hidden path="hscript.n" />
|
||||
<hidden path="hscript.swf" />
|
||||
<hidden path="hscript9.swf" />
|
||||
<hidden path="release.bat" />
|
||||
<hidden path="release.zip" />
|
||||
<hidden path="obj" />
|
||||
</hiddenPaths>
|
||||
<!-- Executed before build -->
|
||||
<preBuildCommand />
|
||||
<!-- Executed after build -->
|
||||
<postBuildCommand alwaysRun="False" />
|
||||
<!-- Other project options -->
|
||||
<options>
|
||||
<option showHiddenPaths="False" />
|
||||
<option testMovie="Default" />
|
||||
<option testMovieCommand="" />
|
||||
</options>
|
||||
<!-- Plugin storage -->
|
||||
<storage />
|
||||
</project>
|
||||
@@ -0,0 +1,536 @@
|
||||
/*
|
||||
* Copyright (C)2008-2017 Haxe Foundation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package hscript;
|
||||
import hscript.Expr;
|
||||
|
||||
|
||||
enum VarMode {
|
||||
Defined;
|
||||
ForceSync;
|
||||
}
|
||||
|
||||
class Async {
|
||||
|
||||
var definedVars : Array<{ n : String, prev : Null<VarMode> }>;
|
||||
var vars : Map<String,VarMode>;
|
||||
var currentFun : String;
|
||||
var currentLoop : Expr;
|
||||
var currentBreak : Expr -> Expr;
|
||||
var uid = 0;
|
||||
public var asyncIdents : Map<String,Bool>;
|
||||
|
||||
static var nullExpr : Expr = #if hscriptPos { e : null, pmin : 0, pmax : 0, origin : "<null>", line : 0 } #else null #end;
|
||||
static var nullId = mk(EIdent("null"), nullExpr);
|
||||
|
||||
inline static function expr( e : Expr ) {
|
||||
return #if hscriptPos e.e #else e #end;
|
||||
}
|
||||
|
||||
inline static function mk( e, inf : Expr ) : Expr {
|
||||
return #if hscriptPos { e : e, pmin : inf.pmin, pmax : inf.pmax, origin : inf.origin, line : inf.line } #else e #end;
|
||||
}
|
||||
|
||||
/**
|
||||
Convert a script into asynchronous one.
|
||||
- calls such as foo(a,b,c) are translated to a_foo(function(r) ...rest, a,b,c) where r is the result value
|
||||
- object access such obj.bar(a,b,c) are translated to obj.a_bar(function(r) ...rest, a, b, c)
|
||||
- @async expr will execute the expression but continue without waiting for it to finish
|
||||
- @split [ e1, e2, e3 ] is transformed to split(function(_) ...rest, [e1, e2, e3]) which
|
||||
should execute asynchronously all expressions - until they return - before continuing the execution
|
||||
- for(i in v) block; loops are translated to the following:
|
||||
var _i = makeIterator(v);
|
||||
function _loop() {
|
||||
if( !_i.hasNext() ) return;
|
||||
var v = _i.next();
|
||||
block(function(_) _loop());
|
||||
}
|
||||
_loop()
|
||||
- while loops are translated similar to for loops
|
||||
- break and continue are correctly handled
|
||||
- you can use @sync <expr> to disable async transformation in some code parts (for performance reason)
|
||||
- a few expressions are still not supported (complex calls, try/catch, and a few others)
|
||||
|
||||
In these examples ...rest represents the continuation of execution of the script after the expression
|
||||
**/
|
||||
public static function toAsync( e : Expr, topLevelSync = false ) {
|
||||
var a = new Async();
|
||||
return a.build(e, topLevelSync);
|
||||
}
|
||||
|
||||
public dynamic function getTopLevelEnd() {
|
||||
return ignore();
|
||||
}
|
||||
|
||||
public function build( e : Expr, topLevelSync = false ) {
|
||||
if( topLevelSync ) {
|
||||
return buildSync(e,null);
|
||||
} else {
|
||||
var end = getTopLevelEnd();
|
||||
return toCps(e, end, end);
|
||||
}
|
||||
}
|
||||
|
||||
function defineVar( v : String, mode ) {
|
||||
definedVars.push({ n : v, prev : vars.get(v) });
|
||||
vars.set(v, mode);
|
||||
}
|
||||
|
||||
function lookupFunctions( el : Array<Expr> ) {
|
||||
for( e in el )
|
||||
switch( expr(e) ) {
|
||||
case EFunction(_, _, name, _) if( name != null ): defineVar(name, Defined);
|
||||
case EMeta("sync",_,expr(_) => EFunction(_,_,name,_)) if( name != null ): defineVar(name, ForceSync);
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
function buildSync( e : Expr, exit : Expr ) : Expr {
|
||||
switch( expr(e) ) {
|
||||
case EFunction(_,_,name,_):
|
||||
if( name != null )
|
||||
return toCps(e, null, null);
|
||||
return e;
|
||||
case EBlock(el):
|
||||
var v = saveVars();
|
||||
lookupFunctions(el);
|
||||
var e = block([for(e in el) buildSync(e,exit)], e);
|
||||
restoreVars(v);
|
||||
return e;
|
||||
case EMeta("async", _, e):
|
||||
return toCps(e, ignore(), ignore());
|
||||
case EMeta("sync", args, ef = expr(_) => EFunction(fargs, body, name, ret)):
|
||||
return mk(EMeta("sync",args,mk(EFunction(fargs, buildSync(body,null), name, ret),ef)),e);
|
||||
case EBreak if( currentBreak != null ):
|
||||
return currentBreak(e);
|
||||
case EContinue if( currentLoop != null ):
|
||||
return block([retNull(currentLoop, e), mk(EReturn(),e)],e);
|
||||
case EFor(_), EWhile(_):
|
||||
var oldLoop = currentLoop, oldBreak = currentBreak;
|
||||
currentLoop = null;
|
||||
currentBreak = null;
|
||||
e = Tools.map(e, buildSync.bind(_, exit));
|
||||
currentLoop = oldLoop;
|
||||
currentBreak = oldBreak;
|
||||
return e;
|
||||
case EReturn(eret) if( exit != null ):
|
||||
return block([eret == null ? retNull(exit, e) : call(exit,[eret], e), mk(EReturn(),e)], e);
|
||||
default:
|
||||
return Tools.map(e, buildSync.bind(_, exit));
|
||||
}
|
||||
}
|
||||
|
||||
public function new() {
|
||||
vars = new Map();
|
||||
definedVars = [];
|
||||
}
|
||||
|
||||
function ignore(?e) : Expr {
|
||||
var inf = e == null ? nullExpr : e;
|
||||
return fun("_", block(e == null ? [] : [e],inf));
|
||||
}
|
||||
|
||||
inline function ident(str, e) {
|
||||
return mk(EIdent(str), e);
|
||||
}
|
||||
|
||||
inline function fun(arg:String, e, ?name) {
|
||||
return mk(EFunction([{ name : arg, t : null, opt: false, value: null }], e, name), e);
|
||||
}
|
||||
|
||||
inline function funs(arg:Array<String>, e, ?name) {
|
||||
return mk(EFunction([for( a in arg ) { name : a, t : null, opt: false, value: null }], e, name), e);
|
||||
}
|
||||
|
||||
inline function block(arr:Array<Expr>, e) {
|
||||
if( arr.length == 1 && expr(arr[0]).match(EBlock(_)) )
|
||||
return arr[0];
|
||||
return mk(EBlock(arr), e);
|
||||
}
|
||||
|
||||
inline function field(e, f, inf) {
|
||||
return mk(EField(e, f), inf);
|
||||
}
|
||||
|
||||
inline function binop(op, e1, e2, inf) {
|
||||
return mk(EBinop(op, e1, e2), inf);
|
||||
}
|
||||
|
||||
inline function call(e, args, inf) {
|
||||
return mk(ECall(e, args), inf);
|
||||
}
|
||||
|
||||
function retNull(e:Expr,?pos) : Expr {
|
||||
switch( expr(e) ) {
|
||||
case EFunction([{name:"_"}], e, _, _): return e;
|
||||
default:
|
||||
}
|
||||
return call(e, [nullId], pos == null ? e : pos);
|
||||
}
|
||||
|
||||
function makeCall( ecall, args : Array<Expr>, rest : Expr, exit, sync = false ) {
|
||||
var names = [for( i in 0...args.length ) "_a"+uid++];
|
||||
var rargs = [for( i in 0...args.length ) ident(names[i],ecall)];
|
||||
if( !sync )
|
||||
rargs.unshift(rest);
|
||||
var rest = mk(sync ? ECall(rest,[call(ecall, rargs,ecall)]) : ECall(ecall, rargs), ecall);
|
||||
var i = args.length - 1;
|
||||
while( i >= 0 ) {
|
||||
rest = toCps(args[i], fun(names[i], rest), exit);
|
||||
i--;
|
||||
}
|
||||
return rest;
|
||||
}
|
||||
|
||||
var syncFlag : Bool;
|
||||
|
||||
function isSync( e : Expr ) {
|
||||
syncFlag = true;
|
||||
checkSync(e);
|
||||
return syncFlag;
|
||||
}
|
||||
|
||||
inline function isAsyncIdent( id : String ) {
|
||||
return asyncIdents == null || asyncIdents.exists(id);
|
||||
}
|
||||
|
||||
function checkSync( e : Expr ) {
|
||||
if( !syncFlag )
|
||||
return;
|
||||
switch( expr(e) ) {
|
||||
case ECall(expr(_) => EIdent(i),_) if( isAsyncIdent(i) || vars.get(i) == Defined ):
|
||||
syncFlag = false;
|
||||
case ECall(expr(_) => EField(_,i),_) if( isAsyncIdent(i) ):
|
||||
syncFlag = false;
|
||||
case EFunction(_,_,name,_) if( name != null ):
|
||||
syncFlag = false;
|
||||
case EMeta("sync" | "async", _, _):
|
||||
// isolated from the sync part
|
||||
default:
|
||||
Tools.iter(e, checkSync);
|
||||
}
|
||||
}
|
||||
|
||||
function saveVars() {
|
||||
return definedVars.length;
|
||||
}
|
||||
|
||||
function restoreVars(k) {
|
||||
while( definedVars.length > k ) {
|
||||
var v = definedVars.pop();
|
||||
if( v.prev == null ) vars.remove(v.n) else vars.set(v.n, v.prev);
|
||||
}
|
||||
}
|
||||
|
||||
public function toCps( e : Expr, rest : Expr, exit : Expr ) : Expr {
|
||||
if( isSync(e) )
|
||||
return call(rest, [buildSync(e, exit)],e);
|
||||
switch( expr(e) ) {
|
||||
case EBlock(el):
|
||||
var el = el.copy();
|
||||
var vold = saveVars();
|
||||
lookupFunctions(el);
|
||||
while( el.length > 0 ) {
|
||||
var e = toCps(el.pop(), rest, exit);
|
||||
rest = ignore(e);
|
||||
}
|
||||
restoreVars(vold);
|
||||
return retNull(rest);
|
||||
case EFunction(args, body, name, t):
|
||||
var vold = saveVars();
|
||||
if( name != null )
|
||||
defineVar(name, Defined);
|
||||
for( a in args )
|
||||
defineVar(a.name, Defined);
|
||||
args.unshift( { name : "_onEnd", t : null, opt: false, value: null } );
|
||||
var frest = ident("_onEnd",e);
|
||||
var oldFun = currentFun;
|
||||
currentFun = name;
|
||||
var body = toCps(body, frest, frest);
|
||||
var f = mk(EFunction(args, body, name, t),e);
|
||||
restoreVars(vold);
|
||||
return rest == null ? f : call(rest, [f],e);
|
||||
case EParent(e):
|
||||
return mk(EParent(toCps(e, rest, exit)),e);
|
||||
case EMeta("sync", _, e):
|
||||
return call(rest,[buildSync(e,exit)],e);
|
||||
case EMeta("async", _, e):
|
||||
var nothing = ignore();
|
||||
return block([toCps(e,nothing,nothing),retNull(rest)],e);
|
||||
case EMeta("split", _, e):
|
||||
var args = switch( expr(e) ) { case EArrayDecl(el): el; default: throw "@split expression should be an array"; };
|
||||
var args = [for( a in args ) fun("_rest", toCps(block([a],a), ident("_rest",a), exit))];
|
||||
return call(ident("split",e), [rest, mk(EArrayDecl(args),e)],e);
|
||||
case ECall(expr(_) => EIdent(i), args):
|
||||
var mode = vars.get(i);
|
||||
return makeCall( ident( mode != null ? i : "a_" + i,e) , args, rest, exit, mode == ForceSync);
|
||||
case ECall(expr(_) => EField(e, f), args):
|
||||
return makeCall(field(e,"a_"+f,e), args, rest, exit);
|
||||
case EFor(v, eit, eloop):
|
||||
var id = ++uid;
|
||||
var it = ident("_i" + id,e);
|
||||
var oldLoop = currentLoop, oldBreak = currentBreak;
|
||||
var loop = ident("_loop" + id,e);
|
||||
currentLoop = loop;
|
||||
currentBreak = function(inf) return block([retNull(rest, inf), mk(EReturn(),inf)], inf);
|
||||
var efor = block([
|
||||
mk(EVar("_i" + id, call(ident("makeIterator",eit),[eit],eit)),eit),
|
||||
fun("_", block([
|
||||
mk(EIf(mk(EUnop("!", true, call( field(it, "hasNext", it), [], it)),it), currentBreak(it)),it),
|
||||
mk(EVar(v, call(field(it, "next",it), [], it)), it),
|
||||
toCps(eloop, loop, exit),
|
||||
], it),"_loop" + id),
|
||||
retNull(loop, e),
|
||||
], e);
|
||||
currentLoop = oldLoop;
|
||||
currentBreak = oldBreak;
|
||||
return efor;
|
||||
case EUnop(op = "!", prefix, eop):
|
||||
return toCps(eop, fun("_r",call(rest, [mk(EUnop(op, prefix, ident("_r",e)),e)], e)), exit);
|
||||
case EBinop(op, e1, e2):
|
||||
switch( op ) {
|
||||
case "=", "+=", "-=", "/=", "*=", "%=", "&=", "|=", "^=":
|
||||
switch( expr(e1) ) {
|
||||
case EIdent(_):
|
||||
var id = "_r" + uid++;
|
||||
return toCps(e2, fun(id, call(rest, [binop(op, e1, ident(id,e1),e1)], e1)), exit);
|
||||
case EField(ef1, f):
|
||||
var id1 = "_r" + uid++;
|
||||
var id2 = "_r" + uid++;
|
||||
return toCps(ef1, fun(id1, toCps(e2, fun(id2, call(rest, [binop(op, field(ident(id1, e1), f, ef1), ident(id2, e2), e)], e)), exit)), exit);
|
||||
case EArray(earr, eindex):
|
||||
var idArr = "_r" + uid++;
|
||||
var idIndex = "_r" + uid++;
|
||||
var idVal = "_r" + uid++;
|
||||
return toCps(earr,fun(idArr, toCps(eindex, fun(idIndex, toCps(e2,
|
||||
fun(idVal, call(rest, [binop(op, mk(EArray(ident(idArr,earr), ident(idIndex,eindex)),e1), ident(idVal,e1), e)], e))
|
||||
, exit)), exit)),exit);
|
||||
default:
|
||||
throw "assert " + e1;
|
||||
}
|
||||
case "||":
|
||||
var id1 = "_r" + uid++;
|
||||
var id2 = "_r" + uid++;
|
||||
return toCps(e1, fun(id1, mk(EIf(binop("==", ident(id1,e1), ident("true",e1), e1),call(rest,[ident("true",e1)],e1),toCps(e2, rest, exit)),e)), exit);
|
||||
case "&&":
|
||||
var id1 = "_r" + uid++;
|
||||
var id2 = "_r" + uid++;
|
||||
return toCps(e1, fun(id1, mk(EIf(binop("!=", ident(id1,e1), ident("true",e1), e1),call(rest,[ident("false",e1)],e1),toCps(e2, rest, exit)),e)), exit);
|
||||
default:
|
||||
var id1 = "_r" + uid++;
|
||||
var id2 = "_r" + uid++;
|
||||
return toCps(e1, fun(id1, toCps(e2, fun(id2, call(rest, [binop(op, ident(id1,e1), ident(id2,e2), e)], e)), exit)), exit);
|
||||
}
|
||||
case EIf(cond, e1, e2), ETernary(cond, e1, e2):
|
||||
return toCps(cond, fun("_c", mk(EIf(ident("_c",cond), toCps(e1, rest, exit), e2 == null ? retNull(rest) : toCps(e2, rest, exit)),e)), exit);
|
||||
case EWhile(cond, ewh):
|
||||
var id = ++uid;
|
||||
var loop = ident("_loop" + id, cond);
|
||||
var oldLoop = currentLoop, oldBreak = currentBreak;
|
||||
currentLoop = loop;
|
||||
currentBreak = function(e) return block([retNull(rest,e), mk(EReturn(),e)],e);
|
||||
var ewhile = block([
|
||||
fun("_r",
|
||||
toCps(cond, fun("_c", mk(EIf(ident("_c", cond), toCps(ewh, loop, exit), retNull(rest,cond)),cond)), exit)
|
||||
, "_loop"+id),
|
||||
retNull(loop, cond),
|
||||
],e);
|
||||
currentLoop = oldLoop;
|
||||
currentBreak = oldBreak;
|
||||
return ewhile;
|
||||
case EReturn(eret):
|
||||
return eret == null ? retNull(exit, e) : toCps(eret, exit, exit);
|
||||
case EObject(fields):
|
||||
var id = "_o" + uid++;
|
||||
var rest = call(rest, [ident(id,e)], e);
|
||||
fields.reverse();
|
||||
for( f in fields )
|
||||
rest = toCps(f.e, fun("_r", block([
|
||||
binop("=", mk(EField(ident(id,f.e), f.name),f.e), ident("_r",f.e), f.e),
|
||||
rest,
|
||||
],f.e)),exit);
|
||||
return block([
|
||||
mk(EVar(id, mk(EObject([]),e)),e),
|
||||
rest,
|
||||
],e);
|
||||
case EArrayDecl(el):
|
||||
var id = "_a" + uid++;
|
||||
var rest = call(rest, [ident(id,e)], e);
|
||||
var i = el.length - 1;
|
||||
while( i >= 0 ) {
|
||||
var e = el[i];
|
||||
rest = toCps(e, fun("_r", block([
|
||||
binop("=", mk(EArray(ident(id,e), mk(EConst(CInt(i)),e)),e), ident("_r",e), e),
|
||||
rest,
|
||||
],e)), exit);
|
||||
i--;
|
||||
}
|
||||
return block([
|
||||
mk(EVar(id, mk(EArrayDecl([]),e)),e),
|
||||
rest,
|
||||
],e);
|
||||
case EArray(earr, eindex):
|
||||
var id1 = "_r" + uid++;
|
||||
var id2 = "_r" + uid++;
|
||||
return toCps(earr, fun(id1, toCps(eindex, fun(id2, call(rest, [mk(EArray(ident(id1,e), ident(id2,e)),e)], e)), exit)), exit);
|
||||
case EVar(v, t, ev):
|
||||
if( ev == null )
|
||||
return block([e, retNull(rest, e)], e);
|
||||
return block([
|
||||
mk(EVar(v, t),e),
|
||||
toCps(ev, fun("_r", block([binop("=", ident(v,e), ident("_r",e), e), retNull(rest,e)], e)), exit),
|
||||
],e);
|
||||
case EConst(_), EIdent(_), EUnop(_), EField(_):
|
||||
return call(rest, [e], e);
|
||||
case ENew(cl, args):
|
||||
var names = [for( i in 0...args.length ) "_a"+uid++];
|
||||
var rargs = [for( i in 0...args.length ) ident(names[i], args[i])];
|
||||
var rest = call(rest,[mk(ENew(cl, rargs),e)],e);
|
||||
var i = args.length - 1;
|
||||
while( i >= 0 ) {
|
||||
rest = toCps(args[i], fun(names[i], rest), exit);
|
||||
i--;
|
||||
}
|
||||
return rest;
|
||||
case EBreak:
|
||||
if( currentBreak == null ) throw "Break outside loop";
|
||||
return currentBreak(e);
|
||||
case EContinue:
|
||||
if( currentLoop == null ) throw "Continue outside loop";
|
||||
return block([retNull(currentLoop, e), mk(EReturn(),e)], e);
|
||||
case ESwitch(v, cases, def):
|
||||
var cases:Array<SwitchCase> = [for( c in cases ) { values : c.values, expr : toCps(c.expr, rest, exit) } ];
|
||||
return toCps(v, mk(EFunction([ { name : "_c", t : null, opt: false, value: null } ], mk(ESwitch(ident("_c",v), cases, def == null ? retNull(rest) : toCps(def, rest, exit)),e)),e), exit );
|
||||
case EThrow(v):
|
||||
return toCps(v, mk(EFunction([ { name : "_v", t : null, opt: false, value: null } ], mk(EThrow(v),v)), v), exit);
|
||||
case EMeta(name,_,e) if( name.charCodeAt(0) == ":".code ): // ignore custom ":" metadata
|
||||
return toCps(e, rest, exit);
|
||||
//case EDoWhile(_), ETry(_), ECall(_):
|
||||
default:
|
||||
throw "Unsupported async expression " + Printer.toString(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class AsyncInterp extends Interp {
|
||||
|
||||
public function setContext( api : Dynamic ) {
|
||||
|
||||
var funs = [];
|
||||
for( v in variables.keys() )
|
||||
if( Reflect.isFunction(variables.get(v)) )
|
||||
funs.push({ v : v, obj : null });
|
||||
|
||||
variables.set("split", split);
|
||||
variables.set("makeIterator", makeIterator);
|
||||
|
||||
var c = Type.getClass(api);
|
||||
for( f in (c == null ? Reflect.fields(api) : Type.getInstanceFields(c)) ) {
|
||||
var fv = Reflect.field(api, f);
|
||||
if( !Reflect.isFunction(fv) ) continue;
|
||||
if( f.charCodeAt(0) == "_".code ) f = f.substr(1);
|
||||
variables.set(f, fv);
|
||||
// create the async wrapper if doesn't exists
|
||||
if( f.substr(0, 2) != "a_" )
|
||||
funs.push({ v : f, obj : api });
|
||||
}
|
||||
|
||||
for( v in funs ) {
|
||||
if( variables.exists("a_" + v.v) ) continue;
|
||||
var fv : Dynamic = variables.get(v.v);
|
||||
var obj = v.obj;
|
||||
variables.set("a_" + v.v, Reflect.makeVarArgs(function(args:Array<Dynamic>) {
|
||||
var onEnd = args.shift();
|
||||
onEnd(Reflect.callMethod(obj, fv, args));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
public function hasMethod( name : String ) {
|
||||
var v = variables.get(name);
|
||||
return v != null && Reflect.isFunction(v);
|
||||
}
|
||||
|
||||
public function callValue( value : Dynamic, args : Array<Dynamic>, ?onResult : Dynamic -> Void, ?vthis : {} ) {
|
||||
var oldThis = variables.get("this");
|
||||
if( vthis != null )
|
||||
variables.set("this", vthis);
|
||||
if( onResult == null )
|
||||
onResult = function(_) {};
|
||||
args.unshift(onResult);
|
||||
Reflect.callMethod(null, value, args);
|
||||
variables.set("this", oldThis);
|
||||
}
|
||||
|
||||
public function callAsync( id : String, args, ?onResult, ?vthis : {} ) {
|
||||
var v = variables.get(id);
|
||||
if( v == null )
|
||||
throw "Missing function " + id + "()";
|
||||
callValue(v, args, onResult, vthis);
|
||||
}
|
||||
|
||||
function split( rest : Dynamic -> Void, args : Array<Dynamic> ) {
|
||||
if( args.length == 0 )
|
||||
rest(null);
|
||||
else {
|
||||
var count = args.length;
|
||||
function next(_) {
|
||||
if( --count == 0 ) rest(null);
|
||||
}
|
||||
for( a in args )
|
||||
a(next);
|
||||
}
|
||||
}
|
||||
|
||||
override function fcall( o : Dynamic, f : String, args : Array<Dynamic> ) : Dynamic {
|
||||
var m = Reflect.field(o, f);
|
||||
if( m == null ) {
|
||||
if( f.substr(0, 2) == "a_" ) {
|
||||
m = Reflect.field(o, f.substr(2));
|
||||
// fallback on sync version
|
||||
if( m != null ) {
|
||||
var onEnd = args.shift();
|
||||
onEnd(call(o, m, args));
|
||||
return null;
|
||||
}
|
||||
// fallback on generic script
|
||||
m = Reflect.field(o, "scriptCall");
|
||||
if( m != null ) {
|
||||
call(o, m, [args.shift(), f.substr(2), args]);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// fallback on generic script
|
||||
m = Reflect.field(o, "scriptCall");
|
||||
if( m != null ) {
|
||||
var result : Dynamic = null;
|
||||
call(o, m, [function(r) result = r, f, args]);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
error(ECustom(o + " has no method " + f));
|
||||
}
|
||||
return call(o, m, args);
|
||||
}
|
||||
|
||||
}
|
||||
+58
-51
@@ -1,26 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Nicolas Cannasse
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* Copyright (C)2008-2017 Haxe Foundation
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 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:
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
* 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.
|
||||
*/
|
||||
package hscript;
|
||||
import hscript.Expr;
|
||||
@@ -30,7 +27,7 @@ class Bytes {
|
||||
var bin : haxe.io.Bytes;
|
||||
var bout : haxe.io.BytesBuffer;
|
||||
var pin : Int;
|
||||
var hstrings : #if haxe3 Map<String,Int> #else Hash<Int> #end;
|
||||
var hstrings : Map<String,Int>;
|
||||
var strings : Array<String>;
|
||||
var nstrings : Int;
|
||||
|
||||
@@ -38,7 +35,7 @@ class Bytes {
|
||||
this.bin = bin;
|
||||
pin = 0;
|
||||
bout = new haxe.io.BytesBuffer();
|
||||
hstrings = #if haxe3 new Map() #else new Hash() #end;
|
||||
hstrings = new Map();
|
||||
strings = [null];
|
||||
nstrings = 1;
|
||||
}
|
||||
@@ -47,7 +44,7 @@ class Bytes {
|
||||
var vid = hstrings.get(v);
|
||||
if( vid == null ) {
|
||||
if( nstrings == 256 ) {
|
||||
hstrings = #if haxe3 new Map() #else new Hash() #end;
|
||||
hstrings = new Map();
|
||||
nstrings = 1;
|
||||
}
|
||||
hstrings.set(v,nstrings);
|
||||
@@ -75,10 +72,7 @@ class Bytes {
|
||||
}
|
||||
|
||||
function doEncodeInt(v: Int) {
|
||||
bout.addByte(v & 0xFF);
|
||||
bout.addByte((v >> 8) & 0xFF);
|
||||
bout.addByte((v >> 16) & 0xFF);
|
||||
bout.addByte(v >>> 24);
|
||||
bout.addInt32(v);
|
||||
}
|
||||
|
||||
function doEncodeConst( c : Const ) {
|
||||
@@ -91,15 +85,6 @@ class Bytes {
|
||||
bout.addByte(1);
|
||||
doEncodeInt(v);
|
||||
}
|
||||
#if !haxe3
|
||||
case CInt32(v):
|
||||
bout.addByte(4);
|
||||
var mid = haxe.Int32.toInt(haxe.Int32.and(v,haxe.Int32.ofInt(0xFFFFFF)));
|
||||
bout.addByte(mid & 0xFF);
|
||||
bout.addByte((mid >> 8) & 0xFF);
|
||||
bout.addByte(mid >> 16);
|
||||
bout.addByte(haxe.Int32.toInt(haxe.Int32.ushr(v, 24)));
|
||||
#end
|
||||
case CFloat(f):
|
||||
bout.addByte(2);
|
||||
doEncodeString(Std.string(f));
|
||||
@@ -110,7 +95,7 @@ class Bytes {
|
||||
}
|
||||
|
||||
function doDecodeInt() {
|
||||
var i = bin.get(pin) | (bin.get(pin+1) << 8) | (bin.get(pin+2) << 16) | (bin.get(pin+3) << 24);
|
||||
var i = bin.getInt32(pin);
|
||||
pin += 4;
|
||||
return i;
|
||||
}
|
||||
@@ -126,13 +111,6 @@ class Bytes {
|
||||
CFloat( Std.parseFloat(doDecodeString()) );
|
||||
case 3:
|
||||
CString( doDecodeString() );
|
||||
#if !haxe3
|
||||
case 4:
|
||||
var i = bin.get(pin) | (bin.get(pin+1) << 8) | (bin.get(pin+2) << 16);
|
||||
var j = bin.get(pin+3);
|
||||
pin += 4;
|
||||
CInt32(haxe.Int32.or(haxe.Int32.ofInt(i), haxe.Int32.shl(haxe.Int32.ofInt(j), 24)));
|
||||
#end
|
||||
default:
|
||||
throw "Invalid code "+bin.get(pin-1);
|
||||
}
|
||||
@@ -146,6 +124,17 @@ class Bytes {
|
||||
#end
|
||||
bout.addByte(Type.enumIndex(e));
|
||||
switch( e ) {
|
||||
case EIgnore(_):
|
||||
case EImport(c):
|
||||
// TODO
|
||||
case EClass(_, _, _, _):
|
||||
// TODO
|
||||
case ERedirect(_, _):
|
||||
// TODO
|
||||
case EUsing(name):
|
||||
// TODO
|
||||
case EEnum(name, fields):
|
||||
// TODO
|
||||
case EConst(c):
|
||||
doEncodeConst(c);
|
||||
case EIdent(v):
|
||||
@@ -247,11 +236,22 @@ class Bytes {
|
||||
}
|
||||
bout.addByte(255);
|
||||
if( def == null ) bout.addByte(255) else doEncode(def);
|
||||
case EMeta(name,args,e):
|
||||
doEncodeString(name);
|
||||
bout.addByte(args == null ? 0 : args.length + 1);
|
||||
if( args != null ) for( e in args ) doEncode(e);
|
||||
doEncode(e);
|
||||
case ECheckType(e,_):
|
||||
doEncode(e);
|
||||
}
|
||||
}
|
||||
|
||||
function doDecode() : Expr {
|
||||
#if hscriptPos
|
||||
if (bin.get(pin) == 255) {
|
||||
pin++;
|
||||
return null;
|
||||
}
|
||||
var origin = doDecodeString();
|
||||
var line = doDecodeInt();
|
||||
return { e : _doDecode(), pmin : 0, pmax : 0, origin : origin, line : line };
|
||||
@@ -269,7 +269,7 @@ class Bytes {
|
||||
case 3:
|
||||
EParent(doDecode());
|
||||
case 4:
|
||||
var a = new Array();
|
||||
var a = [];
|
||||
for( i in 0...bin.get(pin++) )
|
||||
a.push(doDecode());
|
||||
EBlock(a);
|
||||
@@ -286,7 +286,7 @@ class Bytes {
|
||||
EUnop(op,prefix,doDecode());
|
||||
case 8:
|
||||
var e = doDecode();
|
||||
var params = new Array();
|
||||
var params = [];
|
||||
for( i in 0...bin.get(pin++) )
|
||||
params.push(doDecode());
|
||||
ECall(e,params);
|
||||
@@ -308,7 +308,7 @@ class Bytes {
|
||||
case 14:
|
||||
var params = new Array<Argument>();
|
||||
for( i in 0...bin.get(pin++) )
|
||||
params.push({ name : doDecodeString() });
|
||||
params.push({ name : doDecodeString(), opt: false, value: null, t: null });
|
||||
var e = doDecode();
|
||||
var name = doDecodeString();
|
||||
EFunction(params,e,(name == "") ? null: name);
|
||||
@@ -318,13 +318,13 @@ class Bytes {
|
||||
var e = doDecode();
|
||||
EArray(e,doDecode());
|
||||
case 17:
|
||||
var el = new Array();
|
||||
var el = [];
|
||||
for( i in 0...bin.get(pin++) )
|
||||
el.push(doDecode());
|
||||
EArrayDecl(el);
|
||||
case 18:
|
||||
var cl = doDecodeString();
|
||||
var el = new Array();
|
||||
var el = [];
|
||||
for( i in 0...bin.get(pin++) )
|
||||
el.push(doDecode());
|
||||
ENew(cl,el);
|
||||
@@ -335,7 +335,7 @@ class Bytes {
|
||||
var v = doDecodeString();
|
||||
ETry(e,v,null,doDecode());
|
||||
case 21:
|
||||
var fl = new Array();
|
||||
var fl:Array<ObjectField> = [];
|
||||
for( i in 0...bin.get(pin++) ) {
|
||||
var name = doDecodeString();
|
||||
var e = doDecode();
|
||||
@@ -349,7 +349,7 @@ class Bytes {
|
||||
ETernary(cond, e1, e2);
|
||||
case 23:
|
||||
var e = doDecode();
|
||||
var cases = [];
|
||||
var cases:Array<SwitchCase> = [];
|
||||
while( true ) {
|
||||
var v = doDecode();
|
||||
if( v == null ) break;
|
||||
@@ -366,6 +366,13 @@ class Bytes {
|
||||
case 24:
|
||||
var cond = doDecode();
|
||||
EDoWhile(cond,doDecode());
|
||||
case 25:
|
||||
var name = doDecodeString();
|
||||
var count = bin.get(pin++);
|
||||
var args = count == 0 ? null : [for( i in 0...count - 1 ) doDecode()];
|
||||
EMeta(name, args, doDecode());
|
||||
case 26:
|
||||
ECheckType(doDecode(), CTPath(["Void"]));
|
||||
case 255:
|
||||
null;
|
||||
default:
|
||||
|
||||
+1242
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
||||
package hscript;
|
||||
|
||||
class Config {
|
||||
// Runs support for custom classes in these
|
||||
public static final ALLOWED_CUSTOM_CLASSES = [
|
||||
"flixel",
|
||||
];
|
||||
|
||||
// Runs support for abstract support in these
|
||||
public static final ALLOWED_ABSTRACT_AND_ENUM = [
|
||||
"flixel",
|
||||
"openfl.display.BlendMode",
|
||||
];
|
||||
|
||||
// Runs support for using in specific classes.
|
||||
public static final ALLOWED_USING = [
|
||||
|
||||
];
|
||||
|
||||
// Incase any of your files fail
|
||||
// These are the module names
|
||||
public static final DISALLOW_CUSTOM_CLASSES = [
|
||||
|
||||
];
|
||||
|
||||
public static final DISALLOW_ABSTRACT_AND_ENUM = [
|
||||
|
||||
];
|
||||
|
||||
public static final DISALLOW_USING = [
|
||||
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
package hscript;
|
||||
|
||||
import hscript.Interp.DeclaredVar;
|
||||
import hscript.utils.UnsafeReflect;
|
||||
using StringTools;
|
||||
|
||||
@:deprecated('Switch to new Custom Class instead')
|
||||
class CustomClassHandler implements IHScriptCustomConstructor {
|
||||
public static var staticHandler = new StaticHandler();
|
||||
|
||||
public var ogInterp:Interp;
|
||||
public var name:String;
|
||||
public var fields:Array<Expr>;
|
||||
public var extend:String;
|
||||
public var interfaces:Array<String>;
|
||||
|
||||
public var cl:Class<Dynamic>;
|
||||
|
||||
public function new(ogInterp:Interp, name:String, fields:Array<Expr>, ?extend:String, ?interfaces:Array<String>) {
|
||||
this.ogInterp = ogInterp;
|
||||
this.name = name;
|
||||
this.fields = fields;
|
||||
this.extend = extend;
|
||||
this.interfaces = interfaces;
|
||||
|
||||
//this.cl = extend == null ? CustomTemplateClass : Type.resolveClass('${extend}_HSX');
|
||||
if(cl == null)
|
||||
ogInterp.error(EInvalidClass(extend));
|
||||
}
|
||||
|
||||
public function hnew(args:Array<Dynamic>):Dynamic {
|
||||
// TODO: clean this up, it sucks, i hate it
|
||||
// TODO: make static vars work correctly
|
||||
var interp = new Interp();
|
||||
|
||||
interp.errorHandler = ogInterp.errorHandler;
|
||||
|
||||
var _class:IHScriptCustomClassBehaviour = Type.createInstance(cl, args);
|
||||
|
||||
//var __capturedLocals = ogInterp.duplicate(ogInterp.locals);
|
||||
//var capturedLocals:Map<String, DeclaredVar> = [];
|
||||
//for(k=>e in __capturedLocals)
|
||||
// if (e != null && e.depth <= 0)
|
||||
// capturedLocals.set(k, e);
|
||||
|
||||
var disallowCopy = Type.getInstanceFields(cl);
|
||||
|
||||
_class.__real_fields = disallowCopy;
|
||||
|
||||
// todo: make it so you can use variables from the same scope as where the class was defined
|
||||
|
||||
//for (key => value in capturedLocals) {
|
||||
// if(!disallowCopy.contains(key)) {
|
||||
// interp.locals.set(key, {r: value, depth: -1});
|
||||
// }
|
||||
//}
|
||||
for (key => value in ogInterp.variables) {
|
||||
if(!disallowCopy.contains(key)) {
|
||||
interp.variables.set(key, value);
|
||||
}
|
||||
}
|
||||
for(key => value in ogInterp.customClasses) {
|
||||
if(!disallowCopy.contains(key)) {
|
||||
interp.customClasses.set(key, value);
|
||||
}
|
||||
}
|
||||
// todo: clone static vars, but make it so setting it only sets it on the class
|
||||
// todo: clone public vars
|
||||
|
||||
//trace("Before: " + [for(key => value in interp.variables) key]);
|
||||
|
||||
interp.variables.set("super", staticHandler);
|
||||
|
||||
var comparisonMap:Map<String, Dynamic> = [];
|
||||
for(key => value in interp.variables) {
|
||||
comparisonMap.set(key, value);
|
||||
}
|
||||
|
||||
//_class.__custom__variables = interp.variables;
|
||||
|
||||
//trace(fields);
|
||||
for(expr in fields) {
|
||||
@:privateAccess
|
||||
interp.exprReturn(expr);
|
||||
}
|
||||
|
||||
//trace("After: " + [for(key => value in interp.variables) key]);
|
||||
|
||||
// get only variables that were not set before
|
||||
var classVariables = [
|
||||
for(key => value in interp.variables)
|
||||
if(!comparisonMap.exists(key) || comparisonMap[key] != value)
|
||||
key => value
|
||||
];
|
||||
//for(variable => value in classVariables) {
|
||||
// if(variable == "this" || variable == "super" || variable == "new") continue;
|
||||
// @:privateAccess
|
||||
// if(!interp.__instanceFields.contains(variable)) {
|
||||
// interp.__instanceFields.push(variable);
|
||||
// }
|
||||
//}
|
||||
|
||||
//_class.__class__fields = [for(key => value in classVariables) key];
|
||||
|
||||
//trace(_class.__class__fields);
|
||||
//@:privateAccess
|
||||
//trace(interp.__instanceFields);
|
||||
|
||||
//_class.__interp = interp;
|
||||
//_class.__allowSetGet = false;
|
||||
interp.scriptObject = _class;
|
||||
|
||||
for(variable => value in interp.variables) {
|
||||
if(variable == "this" || variable == "super" || variable == "new") continue;
|
||||
|
||||
if(variable.startsWith("set_") || variable.startsWith("get_")) {
|
||||
//_class.__allowSetGet = true;
|
||||
}
|
||||
}
|
||||
|
||||
var newFunc = interp.variables.get("new");
|
||||
if(newFunc != null) {
|
||||
var comparisonMap:Map<String, Dynamic> = [];
|
||||
for(key => value in interp.variables) {
|
||||
comparisonMap.set(key, value);
|
||||
}
|
||||
|
||||
UnsafeReflect.callMethodUnsafe(null, newFunc, args);
|
||||
|
||||
// get only variables that were not set before
|
||||
var classVariables = [
|
||||
for(key => value in interp.variables)
|
||||
if(!comparisonMap.exists(key) || comparisonMap[key] != value)
|
||||
key => value
|
||||
];
|
||||
for(variable => value in classVariables) {
|
||||
if(variable == "this" || variable == "super" || variable == "new") continue;
|
||||
@:privateAccess
|
||||
if(!interp.__instanceFields.contains(variable)) {
|
||||
interp.__instanceFields.push(variable);
|
||||
}
|
||||
/*
|
||||
if(!_class.__class__fields.contains(variable)) {
|
||||
_class.__class__fields.push(variable);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return _class;
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
/*
|
||||
class CustomTemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomAccessBehaviour {
|
||||
public var __interp:Interp;
|
||||
public var __customClass:hscript.customclass.CustomClass;
|
||||
public var __allowSetGet:Bool = true;
|
||||
public var __custom__variables:Map<String, Dynamic>;
|
||||
public var __real_fields:Array<String>;
|
||||
public var __class__fields:Array<String>;
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||
return __callSetter(name, val);
|
||||
if (__custom__variables.exists(name)) {
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
if(__real_fields.contains(name)) {
|
||||
UnsafeReflect.setProperty(this, name, val);
|
||||
return UnsafeReflect.field(this, name);
|
||||
}
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
public function hget(name:String):Dynamic {
|
||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
if (__custom__variables.exists(name))
|
||||
return __custom__variables.get(name);
|
||||
|
||||
return UnsafeReflect.getProperty(this, name);
|
||||
}
|
||||
|
||||
public function __callGetter(name:String):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var v = __custom__variables.get("get_" + name)();
|
||||
__allowSetGet = true;
|
||||
return v;
|
||||
}
|
||||
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var v = __custom__variables.get("set_" + name)(val);
|
||||
__allowSetGet = true;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* This is for backwards compatibility with old hscript-improved, since some scripts use it
|
||||
*/
|
||||
@:dox(hide)
|
||||
@:keep
|
||||
class TemplateClass implements IHScriptCustomBehaviour implements IHScriptCustomAccessBehaviour {
|
||||
public var __interp:Interp;
|
||||
public var __allowSetGet:Bool = true;
|
||||
public var __allowPrivateAccess:Bool = true;
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
var variables = __interp.variables;
|
||||
if(__allowSetGet && variables.exists("set_" + name))
|
||||
return __callSetter(name, val);
|
||||
variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
public function hget(name:String):Dynamic {
|
||||
var variables = __interp.variables;
|
||||
if(__allowSetGet && variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
return variables.get(name);
|
||||
}
|
||||
|
||||
public function __callGetter(name:String):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var v = __interp.variables.get("get_" + name)();
|
||||
__allowSetGet = true;
|
||||
return v;
|
||||
}
|
||||
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var v = __interp.variables.get("set_" + name)(val);
|
||||
__allowSetGet = true;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
/*
|
||||
final class StaticHandler {
|
||||
public function new() {}
|
||||
}
|
||||
*/
|
||||
+170
-70
@@ -1,84 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Nicolas Cannasse
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* Copyright (C)2008-2017 Haxe Foundation
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 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:
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
* 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.
|
||||
*/
|
||||
package hscript;
|
||||
|
||||
typedef Int8 = #if cpp cpp.Int8 #elseif java java.Int8 #elseif cs cs.Int8 #else Int #end;
|
||||
typedef Int16 = #if cpp cpp.Int16 #elseif java java.Int16 #elseif cs cs.Int16 #else Int #end;
|
||||
typedef Int32 = #if cpp cpp.Int32 #else Int #end;
|
||||
typedef Int64 = #if cpp cpp.Int64 #elseif java java.Int64 #elseif cs cs.Int64 #else Int #end;
|
||||
|
||||
typedef UInt8 = #if cpp cpp.UInt8 #elseif cs cs.UInt8 #else Int #end;
|
||||
typedef UInt16 = #if cpp cpp.UInt16 #elseif cs cs.UInt16 #else Int #end;
|
||||
typedef UInt32 = #if cpp cpp.UInt32 #else Int #end;
|
||||
typedef UInt64 = #if cpp cpp.UInt64 #else Int #end;
|
||||
|
||||
enum Const {
|
||||
CInt( v : Int );
|
||||
CFloat( f : Float );
|
||||
CString( s : String );
|
||||
#if !haxe3
|
||||
CInt32( v : haxe.Int32 );
|
||||
#end
|
||||
}
|
||||
|
||||
#if hscriptPos
|
||||
typedef Expr = {
|
||||
var e : ExprDef;
|
||||
var pmin : Int;
|
||||
var pmax : Int;
|
||||
var origin : String;
|
||||
var line : Int;
|
||||
@:structInit
|
||||
final class Expr {
|
||||
public var e : ExprDef;
|
||||
public var pmin : Int;
|
||||
public var pmax : Int;
|
||||
public var origin : String;
|
||||
public var line : Int;
|
||||
}
|
||||
enum ExprDef {
|
||||
#else
|
||||
typedef ExprDef = Expr;
|
||||
enum Expr {
|
||||
#end
|
||||
EIgnore(skip:Bool);
|
||||
EConst( c : Const );
|
||||
EIdent( v : String );
|
||||
EVar( n : String, ?t : CType, ?e : Expr );
|
||||
EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool, ?get : FieldPropertyAccess, ?set : FieldPropertyAccess );
|
||||
EParent( e : Expr );
|
||||
EBlock( e : Array<Expr> );
|
||||
EField( e : Expr, f : String );
|
||||
EField( e : Expr, f : String , ?safe : Bool );
|
||||
EBinop( op : String, e1 : Expr, e2 : Expr );
|
||||
EUnop( op : String, prefix : Bool, e : Expr );
|
||||
ECall( e : Expr, params : Array<Expr> );
|
||||
EIf( cond : Expr, e1 : Expr, ?e2 : Expr );
|
||||
EWhile( cond : Expr, e : Expr );
|
||||
EFor( v : String, it : Expr, e : Expr );
|
||||
EFor( v : String, it : Expr, e : Expr, ?ithv: String);
|
||||
EBreak;
|
||||
EContinue;
|
||||
EFunction( args : Array<Argument>, e : Expr, ?name : String, ?ret : CType );
|
||||
EFunction( args : Array<Argument>, e : Expr, ?name : String, ?ret : CType, ?isPublic : Bool, ?isStatic : Bool, ?isOverride : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool );
|
||||
EReturn( ?e : Expr );
|
||||
EArray( e : Expr, index : Expr );
|
||||
EArrayDecl( e : Array<Expr> );
|
||||
EArrayDecl( e : Array<Expr>, ?wantedType: CType );
|
||||
ENew( cl : String, params : Array<Expr> );
|
||||
EThrow( e : Expr );
|
||||
ETry( e : Expr, v : String, t : Null<CType>, ecatch : Expr );
|
||||
EObject( fl : Array<{ name : String, e : Expr }> );
|
||||
EObject( fl : Array<ObjectField> );
|
||||
ETernary( cond : Expr, e1 : Expr, e2 : Expr );
|
||||
ESwitch( e : Expr, cases : Array<{ values : Array<Expr>, expr : Expr }>, ?defaultExpr : Expr);
|
||||
ESwitch( e : Expr, cases : Array<SwitchCase>, ?defaultExpr : Expr );
|
||||
EDoWhile( cond : Expr, e : Expr);
|
||||
EMeta( name : String, args : Array<Expr>, e : Expr );
|
||||
ECheckType( e : Expr, t : CType );
|
||||
|
||||
EImport( c : String, ?asname:String );
|
||||
EClass( name:String, fields:Array<Expr>, ?extend:String, interfaces:Array<String>, ?isFinal:Bool, ?isPrivate:Bool );
|
||||
ERedirect( name:String, className:String, ?cl:Class<Dynamic> );
|
||||
EUsing(name:String);
|
||||
EEnum(name:String, fields:Array<EnumType>);
|
||||
}
|
||||
|
||||
typedef Argument = { name : String, ?t : CType, ?opt : Bool };
|
||||
@:structInit
|
||||
final class ObjectField {
|
||||
public var name : String;
|
||||
public var e : Expr;
|
||||
}
|
||||
|
||||
@:structInit
|
||||
final class SwitchCase {
|
||||
public var values : Array<Expr>;
|
||||
public var expr : Expr;
|
||||
}
|
||||
|
||||
@:structInit
|
||||
final class Argument {
|
||||
public var name : String;
|
||||
public var t : CType;
|
||||
public var opt : Bool;
|
||||
public var value : Expr;
|
||||
}
|
||||
|
||||
@:structInit
|
||||
final class MetadataEntry {
|
||||
public var name : String;
|
||||
public var params : Array<Expr>;
|
||||
}
|
||||
|
||||
typedef Metadata = Array<MetadataEntry>;
|
||||
|
||||
enum CType {
|
||||
CTPath( path : Array<String>, ?params : Array<CType> );
|
||||
CTFun( args : Array<CType>, ret : CType );
|
||||
CTAnon( fields : Array<{ name : String, t : CType }> );
|
||||
CTAnon( fields : Array<{ name : String, t : CType, ?meta : Metadata }> );
|
||||
CTParent( t : CType );
|
||||
CTOpt( t : CType );
|
||||
CTNamed( n : String, t : CType );
|
||||
}
|
||||
|
||||
#if hscriptPos
|
||||
@@ -95,39 +138,8 @@ class Error {
|
||||
this.origin = origin;
|
||||
this.line = line;
|
||||
}
|
||||
|
||||
private function errorDefToString(): String {
|
||||
switch (e) {
|
||||
case EInvalidChar(c):
|
||||
return "Invalid character: '"+c+"'";
|
||||
|
||||
case EUnexpected(s):
|
||||
return "Unexpected token: \""+s+"\"";
|
||||
|
||||
case EUnterminatedString:
|
||||
return "Unterminated string";
|
||||
|
||||
case EUnterminatedComment:
|
||||
return "Unterminated comment";
|
||||
|
||||
case EUnknownVariable(v):
|
||||
return "Unknown variable: "+v;
|
||||
|
||||
case EInvalidIterator(v):
|
||||
return "Invalid iterator: "+v;
|
||||
|
||||
case EInvalidOp(op):
|
||||
return "Invalid operator: "+op;
|
||||
|
||||
case EInvalidAccess(f):
|
||||
return "Invalid access to field "+f;
|
||||
}
|
||||
}
|
||||
|
||||
public function toString(): String {
|
||||
var message = errorDefToString();
|
||||
message = origin + ":" + line + ": " + message;
|
||||
return message;
|
||||
return Printer.errorToString(this);
|
||||
}
|
||||
}
|
||||
enum ErrorDef {
|
||||
@@ -138,8 +150,96 @@ enum Error {
|
||||
EUnexpected( s : String );
|
||||
EUnterminatedString;
|
||||
EUnterminatedComment;
|
||||
EInvalidPreprocessor( msg : String );
|
||||
EUnknownVariable( v : String );
|
||||
EInvalidIterator( v : String );
|
||||
EInvalidOp( op : String );
|
||||
EInvalidAccess( f : String );
|
||||
ECustom( msg : String );
|
||||
EInvalidClass( className : String);
|
||||
EAlreadyExistingClass( className : String);
|
||||
}
|
||||
|
||||
|
||||
enum ModuleDecl {
|
||||
DPackage( path : Array<String> );
|
||||
DImport( path : Array<String>, ?everything : Bool, ?asname:String );
|
||||
DUsing( path : Array<String>);
|
||||
DTypedef( c : TypeDecl );
|
||||
DClass( c : ClassDecl );
|
||||
}
|
||||
|
||||
typedef ModuleType = {
|
||||
var name : String;
|
||||
var params : {}; // TODO : not yet parsed
|
||||
var meta : Metadata;
|
||||
var isPrivate : Bool;
|
||||
}
|
||||
|
||||
typedef ClassDecl = {> ModuleType,
|
||||
var extend : Null<CType>;
|
||||
var implement : Array<CType>;
|
||||
var fields : Array<FieldDecl>;
|
||||
var isExtern : Bool;
|
||||
}
|
||||
|
||||
typedef TypeDecl = {> ModuleType,
|
||||
var t : CType;
|
||||
}
|
||||
|
||||
typedef FieldDecl = {
|
||||
var name : String;
|
||||
var meta : Metadata;
|
||||
var kind : FieldKind;
|
||||
var access : Array<FieldAccess>;
|
||||
}
|
||||
|
||||
enum abstract FieldAccess(UInt8) {
|
||||
var APublic;
|
||||
var APrivate;
|
||||
var AInline;
|
||||
var AOverride;
|
||||
var AStatic;
|
||||
var AMacro;
|
||||
}
|
||||
|
||||
enum FieldKind {
|
||||
KFunction( f : FunctionDecl );
|
||||
KVar( v : VarDecl );
|
||||
}
|
||||
|
||||
@:structInit
|
||||
final class FunctionDecl {
|
||||
public var args : Array<Argument>;
|
||||
public var body : Expr;
|
||||
public var ret : Null<CType>;
|
||||
}
|
||||
|
||||
typedef VarDecl = {
|
||||
var get : FieldPropertyAccess;
|
||||
var set : FieldPropertyAccess;
|
||||
var isFinal : Bool;
|
||||
var expr : Null<Expr>;
|
||||
var type : Null<CType>;
|
||||
}
|
||||
|
||||
typedef TypeParamDecl = {};
|
||||
|
||||
enum EnumType {
|
||||
ESimple(name: String);
|
||||
EConstructor(name: String, args: Array<Argument>);
|
||||
}
|
||||
|
||||
enum abstract FieldPropertyAccess(UInt8) {
|
||||
var ADefault;
|
||||
var ANull;
|
||||
var AGet;
|
||||
var ASet;
|
||||
var ADynamic;
|
||||
var ANever;
|
||||
}
|
||||
|
||||
enum AccessContext {
|
||||
CInner(cl:String);
|
||||
COuter;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package hscript;
|
||||
|
||||
interface IHScriptCustomAccessBehaviour {
|
||||
var __allowSetGet:Bool;
|
||||
var __allowPrivateAccess:Bool;
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic;
|
||||
public function hget(name:String):Dynamic;
|
||||
|
||||
public function __callGetter(name:String):Dynamic;
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package hscript;
|
||||
|
||||
interface IHScriptCustomBehaviour {
|
||||
public function hset(name:String, val:Dynamic):Dynamic;
|
||||
public function hget(name:String):Dynamic;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package hscript;
|
||||
|
||||
interface IHScriptCustomClassBehaviour {
|
||||
public var __customClass:hscript.customclass.CustomClass;
|
||||
public var __real_fields:Array<String>;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package hscript;
|
||||
|
||||
interface IHScriptCustomConstructor {
|
||||
public function hnew(args:Array<Dynamic>):Dynamic;
|
||||
}
|
||||
+1925
-522
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
package hscript;
|
||||
|
||||
class InterpConfig {
|
||||
// Automatic import redirect for certain classes
|
||||
public static final IMPORT_REDIRECTS = [
|
||||
"Type" => "hscript.proxy.ProxyType"
|
||||
];
|
||||
|
||||
// Incase an import fails
|
||||
// These are the module names
|
||||
@:unreflective
|
||||
public static final DISALLOW_IMPORT = [
|
||||
"Type"
|
||||
];
|
||||
}
|
||||
+53
-64
@@ -1,26 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Nicolas Cannasse
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* Copyright (C)2008-2017 Haxe Foundation
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 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:
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
* 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.
|
||||
*/
|
||||
package hscript;
|
||||
import hscript.Expr.Error;
|
||||
@@ -32,23 +29,13 @@ import haxe.macro.Expr;
|
||||
class Macro {
|
||||
|
||||
var p : Position;
|
||||
#if haxe3
|
||||
var binops : Map<String,Binop>;
|
||||
var unops : Map<String,Unop>;
|
||||
#else
|
||||
var binops : Hash<Binop>;
|
||||
var unops : Hash<Unop>;
|
||||
#end
|
||||
|
||||
public function new(pos) {
|
||||
p = pos;
|
||||
#if haxe3
|
||||
binops = new Map();
|
||||
unops = new Map();
|
||||
#else
|
||||
binops = new Hash();
|
||||
unops = new Hash();
|
||||
#end
|
||||
for( c in Type.getEnumConstructs(Binop) ) {
|
||||
if( c == "OpAssignOp" ) continue;
|
||||
var op = Type.createEnum(Binop, c);
|
||||
@@ -76,9 +63,12 @@ class Macro {
|
||||
case OpMod: assign = true; "%";
|
||||
case OpAssignOp(_): "";
|
||||
case OpInterval: "...";
|
||||
#if haxe3
|
||||
case OpArrow: "=>";
|
||||
#if (haxe_ver >= 4)
|
||||
case OpIn: "in";
|
||||
#end
|
||||
default:
|
||||
continue;
|
||||
};
|
||||
binops.set(str, op);
|
||||
if( assign )
|
||||
@@ -92,20 +82,16 @@ class Macro {
|
||||
case OpNegBits: "~";
|
||||
case OpIncrement: "++";
|
||||
case OpDecrement: "--";
|
||||
#if (haxe_ver >= 4.2)
|
||||
case OpSpread: continue;
|
||||
#end
|
||||
}
|
||||
unops.set(str, op);
|
||||
}
|
||||
}
|
||||
|
||||
#if !haxe3
|
||||
function isType(v:String) {
|
||||
var c0 = v.charCodeAt(0);
|
||||
return c0 >= 'A'.code && c0 <= 'Z'.code;
|
||||
}
|
||||
#end
|
||||
|
||||
function map < T, R > ( a : Array<T>, f : T -> R ) : Array<R> {
|
||||
var b = new Array();
|
||||
var b = [];
|
||||
for( x in a )
|
||||
b.push(f(x));
|
||||
return b;
|
||||
@@ -113,6 +99,7 @@ class Macro {
|
||||
|
||||
function convertType( t : Expr.CType ) : ComplexType {
|
||||
return switch( t ) {
|
||||
case CTOpt(t): TOptional(convertType(t));
|
||||
case CTPath(pack, args):
|
||||
var params = [];
|
||||
if( args != null )
|
||||
@@ -127,10 +114,18 @@ class Macro {
|
||||
case CTParent(t): TParent(convertType(t));
|
||||
case CTFun(args, ret):
|
||||
TFunction(map(args,convertType), convertType(ret));
|
||||
case CTNamed(name, convertType(_) => ct):
|
||||
#if (haxe_ver >= 4)
|
||||
TNamed(name, ct);
|
||||
#else
|
||||
ct;
|
||||
#end
|
||||
case CTAnon(fields):
|
||||
var tf = [];
|
||||
for( f in fields )
|
||||
tf.push( { name : f.name, meta : [], doc : null, access : [], kind : FVar(convertType(f.t),null), pos : p } );
|
||||
for( f in fields ) {
|
||||
var meta = f.meta == null ? [] : [for( m in f.meta ) { name : m.name, params : m.params == null ? [] : [for( e in m.params ) convert(e)], pos : p }];
|
||||
tf.push( { name : f.name, meta : meta, doc : null, access : [], kind : FVar(convertType(f.t), null), pos : p } );
|
||||
}
|
||||
TAnonymous(tf);
|
||||
};
|
||||
}
|
||||
@@ -142,17 +137,9 @@ class Macro {
|
||||
case CInt(v): CInt(Std.string(v));
|
||||
case CFloat(f): CFloat(Std.string(f));
|
||||
case CString(s): CString(s);
|
||||
#if !haxe3
|
||||
case CInt32(v): CInt(Std.string(v));
|
||||
#end
|
||||
});
|
||||
case EIdent(v):
|
||||
#if !haxe3
|
||||
if( isType(v) )
|
||||
EConst(CType(v));
|
||||
else
|
||||
#end
|
||||
EConst(CIdent(v));
|
||||
EConst(CIdent(v));
|
||||
case EVar(n, t, e):
|
||||
EVars([ { name : n, expr : if( e == null ) null else convert(e), type : if( t == null ) null else convertType(t) } ]);
|
||||
case EParent(e):
|
||||
@@ -160,12 +147,7 @@ class Macro {
|
||||
case EBlock(el):
|
||||
EBlock(map(el,convert));
|
||||
case EField(e, f):
|
||||
#if !haxe3
|
||||
if( isType(f) )
|
||||
EType(convert(e), f);
|
||||
else
|
||||
#end
|
||||
EField(convert(e), f);
|
||||
EField(convert(e), f);
|
||||
case EBinop(op, e1, e2):
|
||||
var b = binops.get(op);
|
||||
if( b == null ) throw EInvalidOp(op);
|
||||
@@ -182,14 +164,14 @@ class Macro {
|
||||
EWhile(convert(c), convert(e), true);
|
||||
case EDoWhile(c, e):
|
||||
EWhile(convert(c), convert(e), false);
|
||||
#if (haxe_211 || haxe3)
|
||||
case EFor(v, it, efor):
|
||||
var p = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
|
||||
EFor({ expr : EIn({ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor));
|
||||
#else
|
||||
case EFor(v, it, e):
|
||||
EFor(v, convert(it), convert(e));
|
||||
#end
|
||||
#if (haxe_ver >= 4)
|
||||
var p = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
|
||||
EFor({ expr : EBinop(OpIn,{ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor));
|
||||
#else
|
||||
var p = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
|
||||
EFor({ expr : EIn({ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor));
|
||||
#end
|
||||
case EBreak:
|
||||
EBreak;
|
||||
case EContinue:
|
||||
@@ -203,7 +185,7 @@ class Macro {
|
||||
opt : false,
|
||||
value : null,
|
||||
});
|
||||
EFunction(name, {
|
||||
EFunction(#if haxe4 FNamed(name,false) #else name #end, {
|
||||
params : [],
|
||||
args : targs,
|
||||
expr : convert(e),
|
||||
@@ -231,6 +213,13 @@ class Macro {
|
||||
ETernary(convert(cond), convert(e1), convert(e2));
|
||||
case ESwitch(e, cases, edef):
|
||||
ESwitch(convert(e), [for( c in cases ) { values : [for( v in c.values ) convert(v)], expr : convert(c.expr) } ], edef == null ? null : convert(edef));
|
||||
case EMeta(m, params, esub):
|
||||
var mpos = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
|
||||
EMeta({ name : m, params : params == null ? [] : [for( p in params ) convert(p)], pos : mpos }, convert(esub));
|
||||
case ECheckType(e, t):
|
||||
ECheckType(convert(e), convertType(t));
|
||||
default:
|
||||
null;
|
||||
}, pos : #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end }
|
||||
}
|
||||
|
||||
|
||||
+1758
-521
File diff suppressed because it is too large
Load Diff
+161
-15
@@ -1,3 +1,24 @@
|
||||
/*
|
||||
* Copyright (C)2008-2017 Haxe Foundation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package hscript;
|
||||
import hscript.Expr;
|
||||
|
||||
@@ -9,17 +30,27 @@ class Printer {
|
||||
public function new() {
|
||||
}
|
||||
|
||||
public function exprToString( e : Expr ) {
|
||||
public function exprToString( e : Expr ):String {
|
||||
buf = new StringBuf();
|
||||
tabs = "";
|
||||
expr(e);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
inline function add<T>(s:T) buf.add(s);
|
||||
public function typeToString( t : CType ):String {
|
||||
buf = new StringBuf();
|
||||
tabs = "";
|
||||
type(t);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
function type( t : CType ) {
|
||||
inline function add<T>(s:T):Void buf.add(s);
|
||||
|
||||
function type( t : CType ):Void {
|
||||
switch( t ) {
|
||||
case CTOpt(t):
|
||||
add('?');
|
||||
type(t);
|
||||
case CTPath(path, params):
|
||||
add(path.join("."));
|
||||
if( params != null ) {
|
||||
@@ -31,6 +62,19 @@ class Printer {
|
||||
}
|
||||
add(">");
|
||||
}
|
||||
case CTNamed(name, t):
|
||||
add(name);
|
||||
add(':');
|
||||
type(t);
|
||||
case CTFun(args, ret) if (Lambda.exists(args, function (a) return a.match(CTNamed(_, _)))):
|
||||
add('(');
|
||||
for (a in args)
|
||||
switch a {
|
||||
case CTNamed(_, _): type(a);
|
||||
default: type(CTNamed('_', a));
|
||||
}
|
||||
add(')->');
|
||||
type(ret);
|
||||
case CTFun(args, ret):
|
||||
if( args.length == 0 )
|
||||
add("Void -> ");
|
||||
@@ -57,19 +101,41 @@ class Printer {
|
||||
}
|
||||
}
|
||||
|
||||
function addType( t : CType ) {
|
||||
function addType( t : CType ):Void {
|
||||
if( t != null ) {
|
||||
add(" : ");
|
||||
type(t);
|
||||
}
|
||||
}
|
||||
|
||||
function expr( e : Expr ) {
|
||||
function expr( e : Expr ):Void {
|
||||
if( e == null ) {
|
||||
add("??NULL??");
|
||||
return;
|
||||
}
|
||||
switch( #if hscriptPos e.e #else e #end ) {
|
||||
switch( Tools.expr(e) ) {
|
||||
case EIgnore(_):
|
||||
case EImport(c, n):
|
||||
add("import " + c);
|
||||
if(n != null)
|
||||
add(' as $n');
|
||||
case EClass(name, fields, extend, interfaces):
|
||||
add('class $name');
|
||||
if (extend != null)
|
||||
add(' extends $extend');
|
||||
for(_interface in interfaces) {
|
||||
add(' implements $_interface');
|
||||
}
|
||||
add(' {\n');
|
||||
tabs += "\t";
|
||||
//for(field in fields) {
|
||||
// expr(field);
|
||||
//}
|
||||
|
||||
tabs = tabs.substr(1);
|
||||
add("}");
|
||||
case ERedirect(n, cl):
|
||||
add('typedef $n = $cl');
|
||||
case EConst(c):
|
||||
switch( c ) {
|
||||
case CInt(i): add(i);
|
||||
@@ -78,7 +144,7 @@ class Printer {
|
||||
}
|
||||
case EIdent(v):
|
||||
add(v);
|
||||
case EVar(n, t, e):
|
||||
case EVar(n, t, e): // TODO: static, public, override
|
||||
add("var " + n);
|
||||
addType(t);
|
||||
if( e != null ) {
|
||||
@@ -101,9 +167,9 @@ class Printer {
|
||||
tabs = tabs.substr(1);
|
||||
add("}");
|
||||
}
|
||||
case EField(e, f):
|
||||
case EField(e, f, s):
|
||||
expr(e);
|
||||
add("." + f);
|
||||
add((s == true ? "?." : ".") + f);
|
||||
case EBinop(op, e1, e2):
|
||||
expr(e1);
|
||||
add(" " + op + " ");
|
||||
@@ -154,8 +220,11 @@ class Printer {
|
||||
add(" while ( ");
|
||||
expr(cond);
|
||||
add(" )");
|
||||
case EFor(v, it, e):
|
||||
add("for( "+v+" in ");
|
||||
case EFor(v, it, e, ithv):
|
||||
if(ithv != null)
|
||||
add("for( "+ithv+" => "+v+" in ");
|
||||
else
|
||||
add("for( "+v+" in ");
|
||||
expr(it);
|
||||
add(" ) ");
|
||||
expr(e);
|
||||
@@ -163,7 +232,7 @@ class Printer {
|
||||
add("break");
|
||||
case EContinue:
|
||||
add("continue");
|
||||
case EFunction(params, e, name, ret):
|
||||
case EFunction(params, e, name, ret): // TODO: static, public, override
|
||||
add("function");
|
||||
if( name != null )
|
||||
add(" " + name);
|
||||
@@ -190,7 +259,7 @@ class Printer {
|
||||
add("[");
|
||||
expr(index);
|
||||
add("]");
|
||||
case EArrayDecl(el):
|
||||
case EArrayDecl(el, _):
|
||||
add("[");
|
||||
var first = true;
|
||||
for( e in el ) {
|
||||
@@ -258,11 +327,88 @@ class Printer {
|
||||
add(";\n");
|
||||
}
|
||||
add("}");
|
||||
case EMeta(name, args, e):
|
||||
add("@");
|
||||
add(name);
|
||||
if( args != null && args.length > 0 ) {
|
||||
add("(");
|
||||
var first = true;
|
||||
for( a in args ) {
|
||||
if( first ) first = false else add(", ");
|
||||
expr(e);
|
||||
}
|
||||
add(")");
|
||||
}
|
||||
add(" ");
|
||||
expr(e);
|
||||
case ECheckType(e, t):
|
||||
add("(");
|
||||
expr(e);
|
||||
add(" : ");
|
||||
addType(t);
|
||||
add(")");
|
||||
case EUsing(name):
|
||||
add("using ");
|
||||
add(name);
|
||||
case EEnum(name, params):
|
||||
if (params.length == 0) {
|
||||
add("enum " + name + " {}");
|
||||
return;
|
||||
}
|
||||
add("enum " + name + " {\n");
|
||||
tabs += "\t";
|
||||
for (p in params) {
|
||||
add(tabs);
|
||||
switch p {
|
||||
case EConstructor(name, args):
|
||||
add(name);
|
||||
add("(");
|
||||
for (a in args)
|
||||
addArgument(a);
|
||||
add(")");
|
||||
case ESimple(name):
|
||||
add(name);
|
||||
}
|
||||
add(";\n");
|
||||
}
|
||||
tabs = tabs.substr(1);
|
||||
add(tabs);
|
||||
add("}");
|
||||
}
|
||||
}
|
||||
|
||||
public static function toString( e : Expr ) {
|
||||
function addArgument(a: Argument) {
|
||||
if (a.opt)
|
||||
add("?");
|
||||
add(a.name);
|
||||
addType(a.t);
|
||||
}
|
||||
|
||||
public static function toString( e : Expr ):String {
|
||||
return new Printer().exprToString(e);
|
||||
}
|
||||
|
||||
}
|
||||
public static function errorToString( e : Expr.Error ):String {
|
||||
var message = switch( #if hscriptPos e.e #else e #end ) {
|
||||
case EInvalidChar(c): "Invalid character: '"+(StringTools.isEof(c) ? "EOF (End Of File)" : String.fromCharCode(c))+"' ("+c+")";
|
||||
case EUnexpected(s): "Unexpected token: \""+s+"\"";
|
||||
case EUnterminatedString: "Unterminated string";
|
||||
case EUnterminatedComment: "Unterminated comment";
|
||||
case EInvalidPreprocessor(str): "Invalid preprocessor (" + str + ")";
|
||||
case EUnknownVariable(v): "Unknown variable: "+v;
|
||||
case EInvalidIterator(v): "Invalid iterator: "+v;
|
||||
case EInvalidOp(op): "Invalid operator: "+op;
|
||||
case EInvalidAccess(f): "Invalid access to field " + f;
|
||||
case ECustom(msg): msg;
|
||||
case EInvalidClass(cla): "Invalid class: " + cla + " was not found.";
|
||||
case EAlreadyExistingClass(cla): 'Custom Class named $cla already exists.';
|
||||
};
|
||||
#if hscriptPos
|
||||
return e.origin + ":" + e.line + ": " + message;
|
||||
#else
|
||||
return message;
|
||||
#end
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+200
-8
@@ -1,11 +1,36 @@
|
||||
/*
|
||||
* Copyright (C)2008-2017 Haxe Foundation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package hscript;
|
||||
import hscript.Expr;
|
||||
|
||||
using StringTools;
|
||||
|
||||
class Tools {
|
||||
|
||||
public static function iter( e : Expr, f : Expr -> Void ) {
|
||||
switch( e ) {
|
||||
public static function iter( e : Expr, f : Expr -> Void ):Void {
|
||||
switch( expr(e) ) {
|
||||
case EConst(_), EIdent(_):
|
||||
case EImport(c): f(e);
|
||||
case EClass(_, e, _, _): for( a in e ) f(a);
|
||||
case EVar(_, _, e): if( e != null ) f(e);
|
||||
case EParent(e): f(e);
|
||||
case EBlock(el): for( e in el ) f(e);
|
||||
@@ -34,13 +59,16 @@ class Tools {
|
||||
f(c.expr);
|
||||
}
|
||||
if( def != null ) f(def);
|
||||
case EMeta(name, args, e): if( args != null ) for( a in args ) f(a); f(e);
|
||||
case ECheckType(e,_): f(e);
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
public static function map( e : Expr, f : Expr -> Expr ) {
|
||||
return switch( e ) {
|
||||
case EConst(_), EIdent(_): e;
|
||||
case EVar(n, t, e): EVar(n, t, if( e != null ) f(e) else null);
|
||||
public static function map( e : Expr, f : Expr -> Expr ):Expr {
|
||||
var edef = switch( expr(e) ) {
|
||||
case EConst(_), EIdent(_), EBreak, EContinue: expr(e);
|
||||
case EVar(n, t, e, isPublic, isStatic, isPrivate): EVar(n, t, if( e != null ) f(e) else null, isPublic, isStatic, isPrivate);
|
||||
case EParent(e): EParent(f(e));
|
||||
case EBlock(el): EBlock([for( e in el ) f(e)]);
|
||||
case EField(e, fi): EField(f(e),fi);
|
||||
@@ -51,8 +79,7 @@ class Tools {
|
||||
case EWhile(c, e): EWhile(f(c),f(e));
|
||||
case EDoWhile(c, e): EDoWhile(f(c),f(e));
|
||||
case EFor(v, it, e): EFor(v, f(it), f(e));
|
||||
case EBreak, EContinue: e;
|
||||
case EFunction(args, e, name, t): EFunction(args, f(e), name, t);
|
||||
case EFunction(args, e, name, t, isPublic, isStatic, isOverride, isPrivate): EFunction(args, f(e), name, t, isPublic, isStatic, isOverride, isPrivate);
|
||||
case EReturn(e): EReturn(if( e != null ) f(e) else null);
|
||||
case EArray(e, i): EArray(f(e),f(i));
|
||||
case EArrayDecl(el): EArrayDecl([for( e in el ) f(e)]);
|
||||
@@ -62,7 +89,172 @@ class Tools {
|
||||
case EObject(fl): EObject([for( fi in fl ) { name : fi.name, e : f(fi.e) }]);
|
||||
case ETernary(c, e1, e2): ETernary(f(c), f(e1), f(e2));
|
||||
case ESwitch(e, cases, def): ESwitch(f(e), [for( c in cases ) { values : [for( v in c.values ) f(v)], expr : f(c.expr) } ], def == null ? null : f(def));
|
||||
case EMeta(name, args, e): EMeta(name, args == null ? null : [for( a in args ) f(a)], f(e));
|
||||
case ECheckType(e,t): ECheckType(f(e), t);
|
||||
case EImport(c): EImport(c);
|
||||
case EClass(name, el, extend, interfaces): EClass(name, [for( e in el ) f(e)], extend, interfaces);
|
||||
default: #if hscriptPos e.e #else e #end;
|
||||
}
|
||||
return mk(edef, e);
|
||||
}
|
||||
|
||||
public static inline function expr( e : Expr ) : ExprDef {
|
||||
#if hscriptPos
|
||||
return e.e;
|
||||
#else
|
||||
return e;
|
||||
#end
|
||||
}
|
||||
|
||||
public static inline function mk( e : ExprDef, p : Expr ):Expr {
|
||||
#if hscriptPos
|
||||
return { e : e, pmin : p.pmin, pmax : p.pmax, origin : p.origin, line : p.line };
|
||||
#else
|
||||
return e;
|
||||
#end
|
||||
}
|
||||
|
||||
@:access(hscript.Interp)
|
||||
public static function clearClasses() {
|
||||
#if CUSTOM_CLASSES
|
||||
Interp._customClasses.clear();
|
||||
Interp._customClassAliases.clear();
|
||||
#end
|
||||
}
|
||||
|
||||
public static function removeInnerClass(name: String): String {
|
||||
var ll = name.lastIndexOf(".");
|
||||
if (name.indexOf(".") != ll) { // checks if there are 2 or more dots
|
||||
// wtf did i make -neo
|
||||
// this is awesome -crow
|
||||
return name.substr(0, name.lastIndexOf(".", ll - 1)) + "." + name.substr(ll + 1);
|
||||
}
|
||||
// only one dot or no dots (will work since -1 + 1 = 0)
|
||||
return name.substr(ll + 1);
|
||||
}
|
||||
|
||||
// TODO: maybe use this function for import since also check for innerclasses
|
||||
public static function getClass(name: String): Dynamic {
|
||||
var splitClassName = [for (e in name.split(".")) e.trim()];
|
||||
var realClassName = splitClassName.join(".");
|
||||
|
||||
var c: Dynamic = Type.resolveClass(realClassName);
|
||||
if (c == null) // try importing as enum
|
||||
try
|
||||
c = Type.resolveEnum(name);
|
||||
|
||||
if (c == null) {
|
||||
// lastly try removing any inner class from it
|
||||
// this allows you to import stuff like
|
||||
// flixel.text.FlxText.FlxTextBorderStyle
|
||||
// without the script crashing immediately
|
||||
var className = removeInnerClass(realClassName);
|
||||
if (className != name) {
|
||||
c = Type.resolveClass(className);
|
||||
if (c == null)
|
||||
c = Type.resolveEnum(className);
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
public static function isIterable(v: Dynamic): Bool {
|
||||
// TODO: test for php and lua, they might have issues with this check
|
||||
return v != null && v.iterator != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* DO NOT USE INLINE ON THIS FUNCTION
|
||||
**/
|
||||
public static function argCount(func: haxe.Constraints.Function): Int {
|
||||
#if cpp
|
||||
return untyped __cpp__("{0}->__ArgCount()", func);
|
||||
#elseif js
|
||||
return untyped js.Syntax.code("{0}.length", func);
|
||||
#else
|
||||
return -1;
|
||||
#end
|
||||
}
|
||||
|
||||
public static function getClassDeclFields(classDecl:hscript.Expr.ClassDecl, onlyStatic:Bool = false):Array<FieldDecl> {
|
||||
var fields = classDecl.fields.filter(function(field:FieldDecl) {
|
||||
var isStatic = field.access.contains(AStatic);
|
||||
return onlyStatic ? isStatic : !isStatic;
|
||||
});
|
||||
return fields;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://github.com/pisayesiwsi/hscript-iris/blob/dev/crowplexus/hscript/Tools.hx#L217
|
||||
*/
|
||||
class EnumValue {
|
||||
public var enumName: String;
|
||||
public var name: String;
|
||||
public var index: Int;
|
||||
public var args: Array<Dynamic>;
|
||||
public var enumParent:Dynamic;
|
||||
|
||||
public function new(enumName: String, name: String, index: Int, enumParent:Dynamic, ?args: Array<Dynamic>) {
|
||||
this.enumName = enumName;
|
||||
this.name = name;
|
||||
this.index = index;
|
||||
this.enumParent = enumParent;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public function toString(): String {
|
||||
if (args == null)
|
||||
return enumName + "." + name;
|
||||
return enumName + "." + name + "(" + [for (arg in args) arg].join(", ") + ")";
|
||||
}
|
||||
|
||||
public inline function getEnumName(): String
|
||||
return this.enumName;
|
||||
|
||||
public inline function getConstructorArgs(): Array<Dynamic>
|
||||
return this.args != null ? this.args : [];
|
||||
|
||||
public function compare(other: EnumValue): Bool {
|
||||
if (enumName != other.enumName || name != other.name)
|
||||
return false;
|
||||
if (args == null && other.args == null)
|
||||
return true;
|
||||
if (args == null || other.args == null)
|
||||
return false;
|
||||
if (args.length != other.args.length)
|
||||
return false;
|
||||
|
||||
for (i in 0...args.length) {
|
||||
// TODO: allow deep comparison, like arrays
|
||||
if (args[i] != other.args[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class HScriptEnum implements IHScriptCustomBehaviour {
|
||||
public var enumValues(default, null) = {};
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function setEnum(name:String, enumValue:Dynamic) {
|
||||
Reflect.setField(enumValues, name, enumValue);
|
||||
}
|
||||
|
||||
public function getEnumValue(name:String):Null<Dynamic> {
|
||||
if(Reflect.hasField(enumValues, name)) return Reflect.field(enumValues, name);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function hget(name:String):Dynamic {
|
||||
return getEnumValue(name);
|
||||
}
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,512 @@
|
||||
package hscript.customclass;
|
||||
|
||||
import hscript.utils.UnsafeReflect;
|
||||
import haxe.Constraints.Function;
|
||||
import hscript.Expr;
|
||||
import hscript.Expr.FieldPropertyAccess;
|
||||
import hscript.Expr.VarDecl;
|
||||
import hscript.Expr.FunctionDecl;
|
||||
import hscript.Expr.FieldDecl;
|
||||
|
||||
using Lambda;
|
||||
using StringTools;
|
||||
|
||||
/**
|
||||
* The Custom Class core.
|
||||
*
|
||||
* Provides handlers for custom classes.
|
||||
*
|
||||
* Rest in peace, CustomClassHandler.hx
|
||||
*
|
||||
* Based on Polymod Hscript class system
|
||||
* @see https://github.com/larsiusprime/polymod/tree/master/polymod/hscript/_internal
|
||||
*/
|
||||
@:access(hscript.customclass.CustomClassDecl)
|
||||
class CustomClass implements IHScriptCustomAccessBehaviour {
|
||||
public var interp:Interp;
|
||||
|
||||
public var superClass:Dynamic;
|
||||
public var superConstructor(default, null):Dynamic;
|
||||
|
||||
public var superIsCustomClass(get, never):Bool;
|
||||
private function get_superIsCustomClass():Bool
|
||||
return (superClass != null && superClass is CustomClass);
|
||||
|
||||
public var className(get, never):String;
|
||||
private function get_className():String {
|
||||
return __class.toString();
|
||||
}
|
||||
|
||||
private var __class:CustomClassDecl;
|
||||
private var __cachedSuperFields:Null<Map<String, Dynamic>> = null;
|
||||
|
||||
private var __cachedFieldDecls:Map<String, FieldDecl> = [];
|
||||
private var __cachedFunctionDecls:Map<String, FunctionDecl> = [];
|
||||
private var __cachedVarDecls:Map<String, VarDecl> = [];
|
||||
|
||||
public var accessContext:AccessContext = COuter;
|
||||
public var __allowSetGet:Bool = false;
|
||||
public var __allowPrivateAccess:Bool = false;
|
||||
|
||||
private var isInline(default, null):Bool = false;
|
||||
private var ogVariables(default, null):Map<String, Dynamic>;
|
||||
private var initializing(default, null):Bool = false; // Allows final variables to be initialized
|
||||
|
||||
public function new(__class:CustomClassDecl, args:Array<Dynamic>, ?extendFieldDecl:Map<String, Dynamic>, ?ogInterp:Interp, ?callNew:Bool = true) {
|
||||
this.__class = __class;
|
||||
this.interp = new Interp(this);
|
||||
|
||||
if (ogInterp != null) {
|
||||
interp.importFailedCallback = ogInterp.importFailedCallback;
|
||||
interp.errorHandler = ogInterp.errorHandler;
|
||||
interp.allowStaticVariables = ogInterp.allowStaticVariables;
|
||||
interp.staticVariables = ogInterp.staticVariables;
|
||||
// todo: make it so you can use variables from the same scope as where the class was defined
|
||||
if(__class.isInline != null && __class.isInline) {
|
||||
isInline = __class.isInline;
|
||||
ogVariables = ogInterp.variables;
|
||||
interp.allowPublicVariables = __class.staticInterp.allowPublicVariables;
|
||||
interp.publicVariables = __class.staticInterp.publicVariables;
|
||||
}
|
||||
}
|
||||
|
||||
buildImports();
|
||||
buildUsings();
|
||||
|
||||
if (extendFieldDecl != null)
|
||||
__cachedSuperFields = extendFieldDecl;
|
||||
|
||||
buildClass();
|
||||
|
||||
if (hasFunction('new') && callNew) {
|
||||
buildSuperConstructor();
|
||||
initializing = true;
|
||||
callFunction('new', args);
|
||||
initializing = false;
|
||||
|
||||
if (this.superClass == null && this.__class.classDecl.extend != null)
|
||||
this.interp.error(ECustom("super() not called"));
|
||||
} else if (__class.classDecl.extend != null) {
|
||||
createSuperClass(args);
|
||||
}
|
||||
}
|
||||
|
||||
function buildClass() {
|
||||
if (__cachedSuperFields == null)
|
||||
__cachedSuperFields = [];
|
||||
|
||||
for (f in __class.classDecl.fields) {
|
||||
if (f.access.contains(AStatic))
|
||||
continue; // Skip static field. It's handled by CustomClassDecl.hx
|
||||
__cachedFieldDecls.set(f.name, f);
|
||||
switch (f.kind) {
|
||||
case KFunction(fn):
|
||||
__cachedFunctionDecls.set(f.name, fn);
|
||||
#if hscriptPos
|
||||
var fexpr:Expr = {
|
||||
e: ExprDef.EFunction(fn.args, fn.body, f.name, fn.ret, false, false),
|
||||
pmin: fn.body.pmin,
|
||||
pmax: fn.body.pmax,
|
||||
line: fn.body.line,
|
||||
origin: fn.body.origin
|
||||
};
|
||||
#else
|
||||
var fexpr = Expr.EFunction(fn.args, fn.body, f.name, fn.ret, false, false);
|
||||
#end
|
||||
var func:Function = this.interp.expr(fexpr);
|
||||
this.interp.variables.set(f.name, func);
|
||||
case KVar(v):
|
||||
if(v.get != ADefault || v.set != ADefault)
|
||||
__allowSetGet = true;
|
||||
__cachedVarDecls.set(f.name, v);
|
||||
if (v.expr != null) {
|
||||
var varValue = this.interp.expr(v.expr);
|
||||
this.interp.variables.set(f.name, varValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!__cachedSuperFields.empty()) {
|
||||
for (f => v in __cachedSuperFields) {
|
||||
this.hset(f, v);
|
||||
}
|
||||
__cachedSuperFields.clear();
|
||||
}
|
||||
}
|
||||
|
||||
function buildSuperConstructor() {
|
||||
superConstructor = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
|
||||
createSuperClass(args);
|
||||
});
|
||||
}
|
||||
|
||||
private function createSuperClass(?args:Array<Dynamic>) {
|
||||
if (args == null)
|
||||
args = [];
|
||||
|
||||
if(__class.superClassDecl is CustomClassDecl)
|
||||
superClass = new CustomClass(__class.superClassDecl, args, __cachedSuperFields, this.interp);
|
||||
else {
|
||||
if (__cachedSuperFields != null) {
|
||||
Reflect.setField(__class.superClassDecl, "__cachedFields", __cachedSuperFields); // Static field
|
||||
}
|
||||
|
||||
var disallowCopy = Type.getInstanceFields(__class.superClassDecl);
|
||||
|
||||
superClass = Type.createInstance(__class.superClassDecl, args);
|
||||
superClass.__customClass = this;
|
||||
superClass.__real_fields = disallowCopy;
|
||||
|
||||
if(isInline) {
|
||||
for(s => v in ogVariables)
|
||||
if(!disallowCopy.contains(s))
|
||||
interp.variables.set(s, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildImports() {
|
||||
var i:Int = 0;
|
||||
for (_import in __class.imports) {
|
||||
var importedClass = _import.fullPath;
|
||||
var importAlias = _import.as;
|
||||
|
||||
if (Interp.customClassExist(importedClass) && this.interp.importFailedCallback != null) {
|
||||
this.interp.importFailedCallback(importedClass.split("."), importAlias);
|
||||
continue;
|
||||
}
|
||||
|
||||
#if hscriptPos
|
||||
var e:Expr = {
|
||||
e: ExprDef.EImport(importedClass, importAlias),
|
||||
pmin: 0,
|
||||
pmax: 0,
|
||||
origin: this.className,
|
||||
line: i
|
||||
};
|
||||
#else
|
||||
var e = Expr.EImport(importedClass, importAlias);
|
||||
#end
|
||||
this.interp.expr(e);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
inline function buildUsings() {
|
||||
for (us in __class.usings) {
|
||||
@:privateAccess this.interp.useUsing(us);
|
||||
}
|
||||
}
|
||||
|
||||
public function callFunction(name:String, ?args:Array<Dynamic>):Dynamic {
|
||||
var r:Dynamic = null;
|
||||
|
||||
if (hasField(name)) {
|
||||
var fn = getFunction(name);
|
||||
try {
|
||||
if (fn == null)
|
||||
interp.error(ECustom('${name} is not a function'));
|
||||
|
||||
r = UnsafeReflect.callMethodUnsafe(null, fn, args == null ? [] : args);
|
||||
} catch (e:hscript.Expr.Error) {
|
||||
// A script error occurred while executing the custom class function.
|
||||
// Purge the function from the cache so it is not called again.
|
||||
purgeFunction(name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
var fixedArgs = [];
|
||||
for (a in args) {
|
||||
if ((a is CustomClass)) {
|
||||
var customClass:CustomClass = cast(a, CustomClass);
|
||||
fixedArgs.push(customClass.superClass != null ? customClass.getSuperclass() : customClass);
|
||||
} else {
|
||||
fixedArgs.push(a);
|
||||
}
|
||||
}
|
||||
var superFn:Function = null;
|
||||
if(superClass is CustomClass) {
|
||||
superFn = cast(superClass, CustomClass).hget(name);
|
||||
}
|
||||
else {
|
||||
var fixedName = '_HX_SUPER__${name}';
|
||||
superFn = Reflect.field(superClass, fixedName);
|
||||
}
|
||||
|
||||
if (superFn == null || !Reflect.isFunction(superFn)) {
|
||||
this.interp.error(ECustom('Error while calling function super.${name}(): EInvalidAccess'
|
||||
+ '\n'
|
||||
+ 'InvalidAccess error: Super function "${name}" does not exist! Define it or call the correct superclass function.'));
|
||||
}
|
||||
r = Reflect.callMethod((superClass is CustomClass) ? null : superClass, superFn, fixedArgs);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Field check
|
||||
|
||||
private function hasField(name:String):Bool {
|
||||
return __cachedFieldDecls.exists(name);
|
||||
}
|
||||
|
||||
private function getField(name:String):FieldDecl {
|
||||
return __cachedFieldDecls != null ? __cachedFieldDecls.get(name) : null;
|
||||
}
|
||||
|
||||
private function hasVar(name:String):Bool {
|
||||
return __cachedVarDecls.exists(name);
|
||||
}
|
||||
|
||||
private function getVar(name:String):VarDecl {
|
||||
return __cachedVarDecls.get(name);
|
||||
}
|
||||
|
||||
private function hasFunction(name:String):Bool {
|
||||
return __cachedFunctionDecls.exists(name);
|
||||
}
|
||||
|
||||
private function getFunction(name:String):Function {
|
||||
var fn = this.interp.variables.get(name);
|
||||
return Reflect.isFunction(fn) ? fn : null;
|
||||
}
|
||||
|
||||
// SuperClass field check
|
||||
|
||||
private function cacheSuperField(name:String, value:Dynamic) {
|
||||
if (__cachedSuperFields != null) {
|
||||
__cachedSuperFields.set(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
var __superClassFieldList:Array<String> = null;
|
||||
|
||||
public function superHasField(name:String):Bool {
|
||||
if (superClass == null)
|
||||
return false;
|
||||
|
||||
// Reflect.hasField(this, name) is REALLY expensive so we use a cache.
|
||||
if (__superClassFieldList == null) {
|
||||
__superClassFieldList = [];
|
||||
|
||||
if(superClass is CustomClass) {
|
||||
var cls:Null<Dynamic> = superClass;
|
||||
while(cls != null && cls is CustomClass) {
|
||||
var currentClass = cast(cls, CustomClass);
|
||||
var fields = [for(f in currentClass.__cachedFieldDecls.keys()) f];
|
||||
__superClassFieldList.concat(fields);
|
||||
|
||||
var next = currentClass.superClass;
|
||||
if(next == null)
|
||||
break;
|
||||
cls = next;
|
||||
}
|
||||
|
||||
// The last fetched class is a real class
|
||||
if(!(cls is CustomClass))
|
||||
__superClassFieldList.concat(Reflect.fields(cls).concat(Type.getInstanceFields(Type.getClass(cls))));
|
||||
}
|
||||
else {
|
||||
var realFields = Reflect.fields(superClass).concat(Type.getInstanceFields(Type.getClass(superClass)));
|
||||
__superClassFieldList.concat(realFields);
|
||||
}
|
||||
}
|
||||
|
||||
return __superClassFieldList.indexOf(name) != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a function from the cache.
|
||||
* This is useful when a function is broken and needs to be skipped.
|
||||
* @param name The name of the function to remove from the cache.
|
||||
*/
|
||||
private function purgeFunction(name:String):Void {
|
||||
if (__cachedFunctionDecls != null) {
|
||||
__cachedFunctionDecls.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
// Access fields
|
||||
|
||||
public function hget(name:String):Dynamic {
|
||||
switch (name) {
|
||||
case "superClass": return this.superClass;
|
||||
case "createSuperClass": return this.createSuperClass;
|
||||
case "hasFunction": return this.hasFunction;
|
||||
case "callFunction": return this.callFunction;
|
||||
default:
|
||||
if (hasFunction(name)) {
|
||||
var fn:Function = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
|
||||
return this.callFunction(name, args);
|
||||
});
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
if (hasVar(name)) {
|
||||
var v = getVar(name);
|
||||
var getter = v.get;
|
||||
var isInner = switch (accessContext) {
|
||||
case CInner(cl): return cl == this.className;
|
||||
default: false;
|
||||
}
|
||||
|
||||
var value:Dynamic = null;
|
||||
|
||||
if(getter == ANever || getter == ANull && (!isInner || !__allowPrivateAccess))
|
||||
throw 'field $name cannot be accessed for reading';
|
||||
|
||||
if (__allowSetGet && getter == AGet) {
|
||||
if (hasFunction('get_$name'))
|
||||
value = __callGetter(name);
|
||||
else
|
||||
throw 'Method get_$name required by property $name is missing';
|
||||
}
|
||||
else if (this.interp.variables.exists(name))
|
||||
value = this.interp.variables.get(name);
|
||||
else {
|
||||
if (v.expr != null) {
|
||||
value = this.interp.expr(v.expr);
|
||||
this.interp.variables.set(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
if (this.superClass != null) {
|
||||
if (superHasField(name)) {
|
||||
// Anonymous structure as a super class???
|
||||
if (Type.getClass(this.superClass) == null) {
|
||||
// Anonymous structure
|
||||
if (Reflect.hasField(this.superClass, name))
|
||||
return Reflect.field(this.superClass, name);
|
||||
else
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '[ANONYMOUS STRUCTURE]'";
|
||||
}
|
||||
|
||||
if (this.superClass is CustomClass) {
|
||||
var superCustomClass:CustomClass = cast(this.superClass, CustomClass);
|
||||
|
||||
superCustomClass.__allowSetGet = this.__allowSetGet;
|
||||
superCustomClass.__allowPrivateAccess = this.__allowPrivateAccess;
|
||||
superCustomClass.accessContext = switch (accessContext) {
|
||||
case CInner(_): CInner(superCustomClass.className);
|
||||
default: COuter;
|
||||
}
|
||||
return superCustomClass.hget(name);
|
||||
}
|
||||
|
||||
// Real Class
|
||||
if (__allowSetGet)
|
||||
Reflect.getProperty(this.superClass, name);
|
||||
else
|
||||
Reflect.field(this.superClass, name);
|
||||
}
|
||||
else
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '" + Type.getClassName(Type.getClass(this.superClass)) + "'";
|
||||
}
|
||||
else
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "'";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
switch (name) {
|
||||
default:
|
||||
if (hasVar(name)) {
|
||||
var v = getVar(name);
|
||||
var setter = v.set;
|
||||
var isInner = switch (accessContext) {
|
||||
case CInner(cl): return cl == this.className;
|
||||
default: false;
|
||||
}
|
||||
|
||||
if (setter == ANever || setter == ANull && (!isInner || !__allowPrivateAccess) || (v.isFinal && !initializing))
|
||||
throw 'field $name cannot be accessed for writing';
|
||||
|
||||
if (__allowSetGet && setter == ASet) {
|
||||
if (hasFunction('set_$name'))
|
||||
return __callSetter(name, val);
|
||||
else
|
||||
throw 'Method set_$name required by property $name is missing';
|
||||
}
|
||||
|
||||
this.interp.variables.set(name, val);
|
||||
}
|
||||
else if (this.superClass != null) {
|
||||
if (superHasField(name)) {
|
||||
if (Type.getClass(this.superClass) == null) {
|
||||
// Anonymous structure
|
||||
if (Reflect.hasField(this.superClass, name)) {
|
||||
Reflect.setField(this.superClass, name, val);
|
||||
return val;
|
||||
}
|
||||
else
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '[ANONYMOUS STRUCTURE]'";
|
||||
}
|
||||
|
||||
if (this.superClass is CustomClass) {
|
||||
var superCustomClass:CustomClass = cast(this.superClass, CustomClass);
|
||||
|
||||
superCustomClass.__allowSetGet = this.__allowSetGet;
|
||||
superCustomClass.__allowPrivateAccess = this.__allowPrivateAccess;
|
||||
superCustomClass.accessContext = switch (accessContext) {
|
||||
case CInner(_): CInner(superCustomClass.className);
|
||||
default: COuter;
|
||||
}
|
||||
return superCustomClass.hset(name, val);
|
||||
}
|
||||
// Real Class
|
||||
|
||||
if (__allowSetGet)
|
||||
Reflect.setProperty(this.superClass, name, val);
|
||||
else
|
||||
Reflect.setField(this.superClass, name, val);
|
||||
}
|
||||
else
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '" + Type.getClassName(Type.getClass(this.superClass)) + "'";
|
||||
}
|
||||
else
|
||||
throw "field '" + name + "' does not exist in custom class '" + this.className + "'";
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
public function __callGetter(name:String):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var r = callFunction('get_$name');
|
||||
__allowSetGet = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var r = callFunction('set_$name', [val]);
|
||||
__allowSetGet = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the real superClass if the Custom Class
|
||||
* extends another Custom Class, and so on until
|
||||
* it reaches a real class, otherwise it will
|
||||
* return the last fetched Custom Class
|
||||
* @return Null<Dynamic>
|
||||
*/
|
||||
public function getSuperclass():Null<Dynamic> {
|
||||
var cls:Null<Dynamic> = this.superClass;
|
||||
|
||||
// Check if the superClass is another custom class,
|
||||
// so it will find for a real class, otherwise
|
||||
// returns the last super CustomClass parent.
|
||||
while(cls != null && cls is CustomClass) {
|
||||
var next = cast(cls, CustomClass).superClass;
|
||||
if(next == null)
|
||||
break; // Return the Custom Class itself
|
||||
cls = next;
|
||||
}
|
||||
|
||||
return cls;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
package hscript.customclass;
|
||||
|
||||
import hscript.Expr.AccessContext;
|
||||
import hscript.proxy.ProxyType;
|
||||
import hscript.customclass.utils.FunctionUtils;
|
||||
import haxe.Constraints.Function;
|
||||
import hscript.Expr.FieldDecl;
|
||||
import hscript.Expr.VarDecl;
|
||||
import hscript.Expr.FunctionDecl;
|
||||
|
||||
/**
|
||||
* The ACTUAL "StaticHandler"
|
||||
* @author Jamextreme140
|
||||
*/
|
||||
@:access(hscript.Interp)
|
||||
@:structInit
|
||||
class CustomClassDecl implements IHScriptCustomAccessBehaviour {
|
||||
public var classDecl:Expr.ClassDecl; //This holds the class instantiation info
|
||||
public var imports:Map<String, CustomClassImport>;
|
||||
public var usings:Array<String>;
|
||||
public var pkg:Null<Array<String>> = null;
|
||||
public var ogInterp:Null<Interp> = null;
|
||||
public var isInline:Null<Bool> = null;
|
||||
|
||||
public var staticInterp:Interp = new Interp();
|
||||
|
||||
public var superClassDecl(default, null):Dynamic = null; //This holds the super class reference.
|
||||
|
||||
var _cachedStaticFields:Map<String, FieldDecl> = [];
|
||||
var _cachedStaticFunctions:Map<String, FunctionDecl> = [];
|
||||
var _cachedStaticVariables:Map<String, VarDecl> = [];
|
||||
|
||||
public var __allowSetGet:Bool = true;
|
||||
|
||||
public var __allowPrivateAccess:Bool = false;
|
||||
|
||||
public var accessContext:AccessContext = COuter;
|
||||
|
||||
public final name:String;
|
||||
|
||||
public function new(classDecl:Expr.ClassDecl, imports:Map<String, CustomClassImport>, usings:Array<String>, ?pkg:Array<String>, ?ogInterp:Interp, ?isInline:Bool) {
|
||||
this.classDecl = classDecl;
|
||||
this.imports = imports;
|
||||
this.usings = usings;
|
||||
this.pkg = pkg;
|
||||
this.ogInterp = ogInterp;
|
||||
this.isInline = isInline;
|
||||
|
||||
// Cache the classname
|
||||
var pkg = pkg != null ? '${pkg.join(".")}.' : "";
|
||||
var className = classDecl.name;
|
||||
this.name = '$pkg$className';
|
||||
|
||||
if(ogInterp != null) {
|
||||
staticInterp.importFailedCallback = ogInterp.importFailedCallback;
|
||||
staticInterp.errorHandler = ogInterp.errorHandler;
|
||||
staticInterp.allowStaticVariables = ogInterp.allowStaticVariables;
|
||||
staticInterp.staticVariables = ogInterp.staticVariables;
|
||||
|
||||
if(isInline != null && isInline) {
|
||||
// uses public variables from the same scope as where the class was defined
|
||||
staticInterp.variables = ogInterp.variables;
|
||||
staticInterp.allowPublicVariables = ogInterp.allowPublicVariables;
|
||||
staticInterp.publicVariables = ogInterp.publicVariables;
|
||||
}
|
||||
}
|
||||
|
||||
cacheImports();
|
||||
processUsings();
|
||||
cacheFields();
|
||||
if(classDecl.extend != null)
|
||||
buildSuperClass();
|
||||
}
|
||||
|
||||
function cacheImports() {
|
||||
// This will make imported classes available for Static Functions
|
||||
var i:Int = 0;
|
||||
for(s => imp in imports) {
|
||||
var importedClass = imp.fullPath;
|
||||
var importAlias = imp.as;
|
||||
|
||||
if(this.staticInterp.variables.exists(imp.name)) continue; // class is already imported
|
||||
|
||||
if (Interp.customClassExist(importedClass) && this.staticInterp.importFailedCallback != null) {
|
||||
this.staticInterp.importFailedCallback(importedClass.split("."), importAlias);
|
||||
continue;
|
||||
}
|
||||
|
||||
#if hscriptPos
|
||||
var e:Expr = {
|
||||
e: ExprDef.EImport(importedClass, importAlias),
|
||||
pmin: 0,
|
||||
pmax: 0,
|
||||
origin: this.classDecl.name,
|
||||
line: i
|
||||
};
|
||||
i++;
|
||||
#else
|
||||
var e = Expr.EImport(importedClass, importAlias);
|
||||
#end
|
||||
this.staticInterp.expr(e);
|
||||
}
|
||||
}
|
||||
|
||||
function cacheFields() {
|
||||
for (f in classDecl.fields) {
|
||||
if (f.access.contains(AStatic)) {
|
||||
_cachedStaticFields.set(f.name, f);
|
||||
switch (f.kind) {
|
||||
case KFunction(fn):
|
||||
_cachedStaticFunctions.set(f.name, fn);
|
||||
#if hscriptPos
|
||||
var fexpr:Expr = {
|
||||
e: ExprDef.EFunction(fn.args, fn.body, f.name, fn.ret, false, false),
|
||||
pmin: fn.body.pmin,
|
||||
pmax: fn.body.pmax,
|
||||
line: fn.body.line,
|
||||
origin: fn.body.origin
|
||||
};
|
||||
#else
|
||||
var fexpr = Expr.EFunction(fn.args, fn.body, f.name, fn.ret, false, false);
|
||||
#end
|
||||
var func:Function = this.staticInterp.expr(fexpr);
|
||||
this.staticInterp.variables.set(f.name, func);
|
||||
case KVar(v):
|
||||
if(v.get != ADefault || v.set != ADefault)
|
||||
__allowSetGet = true;
|
||||
_cachedStaticVariables.set(f.name, v);
|
||||
if (v.expr != null) {
|
||||
var varValue = this.staticInterp.expr(v.expr);
|
||||
this.staticInterp.variables.set(f.name, varValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processUsings() {
|
||||
for(us in usings) {
|
||||
this.staticInterp.useUsing(us);
|
||||
}
|
||||
}
|
||||
|
||||
function buildSuperClass() {
|
||||
var extendString = new Printer().typeToString(classDecl.extend);
|
||||
if (this.pkg != null && extendString.indexOf(".") == -1) {
|
||||
extendString = this.pkg.join(".") + "." + extendString;
|
||||
}
|
||||
|
||||
var cls:Dynamic = Type.resolveClass('${extendString}_HSX');
|
||||
if(cls == null)
|
||||
cls = ProxyType.resolveClass(extendString);
|
||||
|
||||
superClassDecl = cls;
|
||||
|
||||
if(superClassDecl == null)
|
||||
staticInterp.error(ECustom("could not resolve super class: " + extendString));
|
||||
}
|
||||
|
||||
public function callFunction(name:String, ?args:Array<Dynamic>):Dynamic {
|
||||
var func:Function = getFunction(name);
|
||||
|
||||
return FunctionUtils.callStaticFunction(name, this, staticInterp, func, args != null ? args : []);
|
||||
}
|
||||
|
||||
public function hasField(name:String):Bool {
|
||||
return _cachedStaticFields.exists(name);
|
||||
}
|
||||
|
||||
private function hasFunction(name:String) {
|
||||
return _cachedStaticFunctions.exists(name);
|
||||
}
|
||||
|
||||
private function getFunction(name:String):Function {
|
||||
var fn = this.staticInterp.variables.get(name);
|
||||
return Reflect.isFunction(fn) ? fn : null;
|
||||
}
|
||||
|
||||
private function hasVar(name:String):Bool {
|
||||
return _cachedStaticVariables.exists(name);
|
||||
}
|
||||
|
||||
private function getVar(name:String):VarDecl {
|
||||
return _cachedStaticVariables.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a function from the cache.
|
||||
* This is useful when a function is broken and needs to be skipped.
|
||||
* @param name The name of the function to remove from the cache.
|
||||
*/
|
||||
private function purgeFunction(name:String):Void {
|
||||
if (_cachedStaticFunctions != null) {
|
||||
_cachedStaticFunctions.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public function hget(name:String):Dynamic {
|
||||
if(hasVar(name)) {
|
||||
var v = getVar(name);
|
||||
var getter = v.get;
|
||||
var isInner = switch (accessContext) {
|
||||
case CInner(cl): return cl == this.toString();
|
||||
default: false;
|
||||
}
|
||||
|
||||
var r:Dynamic = null;
|
||||
|
||||
if (getter == ANever || getter == ANull && (!isInner || !__allowPrivateAccess))
|
||||
throw 'field $name cannot be accessed for reading';
|
||||
|
||||
if(__allowSetGet && getter == AGet){
|
||||
if(hasFunction('get_$name'))
|
||||
r = __callGetter(name);
|
||||
else
|
||||
throw 'Method get_$name required by property $name is missing';
|
||||
}
|
||||
else if (this.staticInterp.variables.exists(name))
|
||||
r = this.staticInterp.variables.get(name);
|
||||
else {
|
||||
if(v.expr != null) {
|
||||
r = this.staticInterp.expr(v.expr);
|
||||
this.staticInterp.variables.set(name, r);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
if(hasFunction(name)) {
|
||||
// TODO: optimize this
|
||||
var fn:Function = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
|
||||
return this.callFunction(name, args);
|
||||
});
|
||||
return fn;
|
||||
}
|
||||
|
||||
throw "static field '" + name + "' does not exist in custom class '" + this.classDecl.name + "'";
|
||||
}
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
if (hasVar(name)) {
|
||||
var v = getVar(name);
|
||||
var setter = v.set;
|
||||
var isInner = switch (accessContext) {
|
||||
case CInner(cl): return cl == this.toString();
|
||||
default: false;
|
||||
}
|
||||
|
||||
if (setter == ANever || setter == ANull && (!isInner || !__allowPrivateAccess) || v.isFinal)
|
||||
throw 'field $name cannot be accessed for writing';
|
||||
|
||||
if (__allowSetGet && setter == ASet) {
|
||||
if (hasFunction('set_$name'))
|
||||
return __callSetter(name, val);
|
||||
else
|
||||
throw 'Method set_$name required by property $name is missing';
|
||||
}
|
||||
else {
|
||||
this.staticInterp.variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
throw "static field '" + name + "' does not exist in custom class '" + this.classDecl.name + "'";
|
||||
}
|
||||
|
||||
public function __callGetter(name:String):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var r = callFunction('get_${name}');
|
||||
__allowSetGet = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var r = callFunction('set_${name}', [val]);
|
||||
__allowSetGet = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
typedef CustomClassImport = {
|
||||
var ?name:String;
|
||||
var ?pkg:Array<String>;
|
||||
var ?fullPath:String; // pkg.pkg.pkg.name
|
||||
var ?as:Null<String>; // import pkg.Name as OtherName
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package hscript.customclass.utils;
|
||||
|
||||
import hscript.utils.UnsafeReflect;
|
||||
import haxe.Constraints.Function;
|
||||
|
||||
@:access(hscript.customclass.CustomClassDecl)
|
||||
class FunctionUtils {
|
||||
public static inline function callStaticFunction(name:String, classDecl:CustomClassDecl, interp:Interp, fn:Function, args:Array<Dynamic> = null) {
|
||||
var r:Dynamic = null;
|
||||
|
||||
try {
|
||||
if (fn == null)
|
||||
interp.error(ECustom('${name} is not a function'));
|
||||
|
||||
r = UnsafeReflect.callMethodUnsafe(null, fn, args);
|
||||
} catch (e:hscript.Expr.Error) {
|
||||
// A script error occurred while executing the custom class function.
|
||||
// Purge the function from the cache so it is not called again.
|
||||
classDecl.purgeFunction(name);
|
||||
interp.error(#if hscriptPos e.e #else e #end);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package hscript.macros;
|
||||
|
||||
#if macro
|
||||
import Type.ValueType;
|
||||
import haxe.macro.ComplexTypeTools;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Printer;
|
||||
import haxe.macro.Compiler;
|
||||
|
||||
using StringTools;
|
||||
|
||||
class AbstractHandler {
|
||||
public static function init() {
|
||||
#if !display
|
||||
if(Context.defined("display")) return;
|
||||
for(apply in Config.ALLOWED_ABSTRACT_AND_ENUM) {
|
||||
Compiler.addGlobalMetadata(apply, '@:build(hscript.macros.AbstractHandler.build())');
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
public static function build():Array<Field> {
|
||||
var fields = Context.getBuildFields();
|
||||
var clRef = Context.getLocalClass();
|
||||
if (clRef == null) return fields;
|
||||
var cl = clRef.get();
|
||||
|
||||
if (/* cl.name.startsWith("Flx") && */ cl.name.endsWith("_Impl_") && cl.params.length <= 0 && !cl.meta.has(":multiType") && !cl.name.contains("_HSC")) {
|
||||
var metas = cl.meta.get();
|
||||
|
||||
var trimEnum = cl.name.substr(0, cl.name.length - 6);
|
||||
|
||||
var key = cl.module;
|
||||
var fkey = cl.module + "." + trimEnum;
|
||||
if(key == "lime.system.Locale") return fields; // Error: Unknown identifier : currentLocale, Due to Func
|
||||
if(key == "cpp.Function") return fields; // Error: Unknown identifier : nativeGetProcAddress, Due to Func
|
||||
if(key == "haxe.ds.Vector") return fields; // Error: haxe.ds._Vector.VectorData<blit.T> has no field blit, Due to Func
|
||||
if(key == "haxe.display.Display") return fields; // Error: haxe.display.DisplayItemKind<haxe.display.DisplayLiteral<Dynamic>> has no field Null, Due to Func
|
||||
if(key == "cpp.Callable") return fields; // Error: cpp.Function.fromStaticFunction must be called on static function, Due to Func
|
||||
if(key == "haxe.display.JsonAnonStatusKind") return fields; // Error: cannot initialize a variable of type 'char *' with an rvalue of type 'const char *', Due to Func
|
||||
if(key == "cpp.CharStar") return fields; // Error: cannot initialize a variable of type 'char *' with an rvalue of type 'const char *', Due to Func
|
||||
if(Config.DISALLOW_ABSTRACT_AND_ENUM.contains(cl.module) || Config.DISALLOW_ABSTRACT_AND_ENUM.contains(fkey)) return fields;
|
||||
if(cl.module.contains("_")) return fields; // Weird issue, sorry
|
||||
|
||||
var shadowClass = macro class {
|
||||
|
||||
};
|
||||
shadowClass.kind = TDClass();
|
||||
shadowClass.params = switch(cl.params.length) {
|
||||
case 0:
|
||||
null;
|
||||
case 1:
|
||||
[{
|
||||
name: "T",
|
||||
}];
|
||||
default:
|
||||
[for(k=>e in cl.params) {
|
||||
name: "T" + Std.int(k+1)
|
||||
}];
|
||||
};
|
||||
shadowClass.name = '${cl.name.substr(0, cl.name.length - 6)}_HSC';
|
||||
|
||||
var imports = Context.getLocalImports().copy();
|
||||
Utils.setupMetas(shadowClass, imports);
|
||||
//trace(cl.module);
|
||||
|
||||
for(f in fields)
|
||||
switch(f.kind) {
|
||||
case FFun(fun):
|
||||
if (f.access.contains(AStatic)) {
|
||||
if (fun.expr != null) {
|
||||
fun.expr = macro @:privateAccess $e{fun.expr};
|
||||
shadowClass.fields.push(f);
|
||||
|
||||
/*var trimEnum = cl.name.substr(0, cl.name.length - 6);
|
||||
|
||||
var returns:Bool = !fun.ret.match(TPath({name: "Void"}));
|
||||
|
||||
var name = f.name;
|
||||
|
||||
var arguments = fun.args == null ? [] : [for(a in fun.args) macro $i{a.name}];
|
||||
|
||||
var expr:Expr = Context.parse('${returns?"return " : ""} $trimEnum.$name(${[for(a in fun.args) a.name].join(", ")})', f.pos);
|
||||
|
||||
var func:Function = {
|
||||
ret: fun.ret,
|
||||
params: fun.params.copy(),
|
||||
expr: expr,
|
||||
args: fun.args.copy()
|
||||
};
|
||||
|
||||
var field:Field = {
|
||||
pos: f.pos,
|
||||
name: f.name,
|
||||
meta: f.meta,
|
||||
kind: FFun(func),
|
||||
doc: null,//f.doc,
|
||||
access: [APublic, AStatic]
|
||||
}
|
||||
shadowClass.fields.push(field);*/
|
||||
}
|
||||
}
|
||||
case FProp(get, set, t, e):
|
||||
if (get == "default" && (set == "never" || set == "null")) {
|
||||
shadowClass.fields.push(f);
|
||||
}
|
||||
case FVar(t, e):
|
||||
if (f.access.contains(AStatic) || cl.meta.has(":enum") || f.name.toUpperCase() == f.name) {
|
||||
var name:String = f.name;
|
||||
var enumType:String = cl.name;
|
||||
var pack = cl.module.split(".");
|
||||
|
||||
//trace(pack, cl.name, name, cl.module);
|
||||
|
||||
if(pack[pack.length - 1] == trimEnum)
|
||||
pack.pop();
|
||||
|
||||
var complexType:ComplexType = t;
|
||||
if(complexType == null && e != null) {
|
||||
complexType = switch(e.expr) {
|
||||
case EConst(CRegexp(_)): TPath({ name: "EReg", pack: [] });
|
||||
|
||||
default: null;
|
||||
}
|
||||
}
|
||||
if(complexType == null) {
|
||||
complexType = TPath({
|
||||
name: trimEnum,
|
||||
pack: [],//pack
|
||||
});
|
||||
}
|
||||
|
||||
var code = Context.parse('@:privateAccess ($trimEnum.$name)', f.pos); // '${pack.join(".")}.${trimEnum}.$name'
|
||||
|
||||
var field:Field = {
|
||||
pos: f.pos,
|
||||
name: f.name,
|
||||
meta: f.meta,
|
||||
kind: FVar(null, code),
|
||||
doc: f.doc,
|
||||
access: [APublic, AStatic]
|
||||
}
|
||||
|
||||
shadowClass.fields.push(field);
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
//var printer = new Printer();
|
||||
//for(field in shadowClass.fields)
|
||||
// trace(printer.printField(field));
|
||||
|
||||
Context.defineModule(cl.module, [shadowClass], imports);
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -0,0 +1,666 @@
|
||||
package hscript.macros;
|
||||
|
||||
#if macro
|
||||
import haxe.macro.Type.ClassType;
|
||||
import Type.ValueType;
|
||||
import haxe.macro.Expr.Function;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Type.MetaAccess;
|
||||
import haxe.macro.Type.FieldKind;
|
||||
import haxe.macro.Type.ClassField;
|
||||
import haxe.macro.Type.VarAccess;
|
||||
import haxe.macro.*;
|
||||
import Sys;
|
||||
|
||||
using StringTools;
|
||||
|
||||
class ClassExtendMacro {
|
||||
public static inline final FUNC_PREFIX = "_HX_SUPER__";
|
||||
public static inline final CLASS_SUFFIX = "_HSX";
|
||||
|
||||
public static var unallowedMetas:Array<String> = [":bitmap", ":noCustomClass", ":generic"];
|
||||
|
||||
public static var modifiedClasses:Array<String> = [];
|
||||
|
||||
public static function init() {
|
||||
#if !display
|
||||
#if CUSTOM_CLASSES
|
||||
if(Context.defined("display")) return;
|
||||
for(apply in Config.ALLOWED_CUSTOM_CLASSES) {
|
||||
Compiler.addGlobalMetadata(apply, "@:build(hscript.macros.ClassExtendMacro.build())");
|
||||
}
|
||||
#end
|
||||
#end
|
||||
}
|
||||
|
||||
public static function build():Array<Field> {
|
||||
var fields = Context.getBuildFields();
|
||||
var clRef = Context.getLocalClass();
|
||||
if (clRef == null) return fields;
|
||||
var cl = clRef.get();
|
||||
|
||||
if (cl.isAbstract || cl.isExtern || cl.isFinal || cl.isInterface) return fields;
|
||||
if (!cl.name.endsWith("_Impl_") && !cl.name.endsWith(CLASS_SUFFIX) && !cl.name.endsWith("_HSC")) {
|
||||
var metas = cl.meta.get();
|
||||
|
||||
for(m in metas)
|
||||
if (unallowedMetas.contains(m.name))
|
||||
return fields;
|
||||
|
||||
if(cl.params.length > 0)
|
||||
return fields;
|
||||
|
||||
var key = cl.module;
|
||||
var fkey = cl.module + "." + cl.name;
|
||||
if(key == "sys.thread.FixedThreadPool") return fields; // Error: Type name sys.thread.Worker_HSX is redefined from module sys.thread.FixedThreadPool
|
||||
if(key == "StdTypes") return fields; // Error: Cant extend basic class
|
||||
if(key == "Xml") return fields; // Error: Cant extend basic class
|
||||
if(key == "Date") return fields; // Error: Cant extend basic class
|
||||
if(key == "away3d.tools.commands.Mirror") return fields; // Error: Unknown identifier
|
||||
if(key == "away3d.tools.commands.SphereMaker") return fields; // Error: Unknown identifier
|
||||
if(key == "away3d.tools.commands.Weld") return fields; // Error: Unknown identifier
|
||||
if(fkey == "hscript.CustomClassHandler.TemplateClass") return fields; // Error: Redefined
|
||||
if(fkey == "hscript.CustomClassHandler.CustomTemplateClass") return fields; // Error: Redefined
|
||||
if(key == "sys.thread.EventLoop") return fields; // Error: cant override force inlined
|
||||
if(Config.DISALLOW_CUSTOM_CLASSES.contains(cl.module) || Config.DISALLOW_CUSTOM_CLASSES.contains(fkey)) return fields;
|
||||
if(cl.module.contains("_")) return fields; // Weird issue, sorry
|
||||
|
||||
var superFields = [];
|
||||
if(false && cl.superClass != null) {
|
||||
var _superFields = cl.superClass.t.get().fields.get();
|
||||
_superFields = []; // Comment to enable super support, (broken)
|
||||
|
||||
function convertField(field:ClassField) {
|
||||
try {
|
||||
var nfield = FixedTypeTools.toSimpleField(field);
|
||||
switch ([field.kind, field.type]) {
|
||||
case [FMethod(kind), TFun(args, ret)]:
|
||||
if(kind == MethInline)
|
||||
nfield.access.push(AInline);
|
||||
if(kind == MethDynamic)
|
||||
nfield.access.push(ADynamic);
|
||||
default:
|
||||
}
|
||||
|
||||
switch(nfield.kind) {
|
||||
case FFun(fun):
|
||||
if (fun.params != null && fun.params.length > 0)
|
||||
return null;
|
||||
|
||||
//sfun.ret = Utils.fixStdTypes(fun.ret);
|
||||
|
||||
var metas = nfield.meta;
|
||||
var defaultValues:Map<String, Dynamic> = [];
|
||||
var defaultEntry = null;
|
||||
var isGeneric = false;
|
||||
for(m in metas) {
|
||||
if(m.name == ":value") {
|
||||
defaultEntry = m;
|
||||
switch(m.params[0].expr) {
|
||||
case EObjectDecl(fields):
|
||||
for(fil in fields)
|
||||
defaultValues[fil.field] = fil.expr;
|
||||
default:
|
||||
}
|
||||
}
|
||||
if(m.name == ":generic")
|
||||
isGeneric = true;
|
||||
}
|
||||
if(isGeneric) return null;
|
||||
|
||||
if(defaultEntry != null)
|
||||
metas.remove(defaultEntry);
|
||||
|
||||
for(arg in fun.args) {
|
||||
var opt = false;
|
||||
if(defaultValues.exists(arg.name)) {
|
||||
arg.value = defaultValues[arg.name];
|
||||
arg.opt = false;
|
||||
}
|
||||
|
||||
arg.type = null;//Utils.fixStdTypes(arg.type);
|
||||
|
||||
//if(arg.opt) {
|
||||
// if(arg.type.getParameters()[0].name != "Null")
|
||||
// arg.type = TPath({name: "Null", params: [TPType(arg.type)], pack: []});//macro {Null<Dynamic>};
|
||||
//}
|
||||
}
|
||||
|
||||
trace(nfield.name);
|
||||
default:
|
||||
}
|
||||
return nfield;
|
||||
} catch(e) {
|
||||
trace(field.name, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var didPrint = false;
|
||||
|
||||
var fieldNames = [for(f in fields) f.name];
|
||||
|
||||
/*for(field in _superFields) {
|
||||
if(fieldNames.contains(field.name))
|
||||
continue;
|
||||
|
||||
if(!field.kind.match(FMethod(_))) // only catch methods
|
||||
continue;
|
||||
|
||||
if(field.name.startsWith("get_")) {
|
||||
var access = FixedTypeTools.getAccess(field);
|
||||
if(access.contains(AInline) || access.contains(AFinal) || field.isFinal)
|
||||
continue;
|
||||
var name = field.name;
|
||||
superFields.push({
|
||||
name: field.name,
|
||||
pos: field.pos,
|
||||
kind: FFun({
|
||||
ret: null,
|
||||
params: [],
|
||||
expr: macro {
|
||||
return super.$name();
|
||||
},
|
||||
args: []
|
||||
}),
|
||||
access: access,
|
||||
meta: field.meta.get(),
|
||||
});
|
||||
//var f = convertField(field);
|
||||
//if(f != null)
|
||||
// superFields.push(f);
|
||||
if(field.name == "get_bgColor") {
|
||||
if(!didPrint) {
|
||||
trace(cl.name);
|
||||
didPrint = true;
|
||||
}
|
||||
trace("> " + field.name + " : " + access, field);
|
||||
}
|
||||
}
|
||||
|
||||
}*/
|
||||
|
||||
// want to get this working
|
||||
/*for(field in _superFields) {
|
||||
if(fieldNames.contains(field.name))
|
||||
continue;
|
||||
|
||||
if(!field.kind.match(FMethod(_))) // only catch methods
|
||||
continue;
|
||||
|
||||
var f = convertField(field);
|
||||
if(f != null)
|
||||
superFields.push(f);
|
||||
}*/
|
||||
//superFields = [];
|
||||
}
|
||||
|
||||
var shadowClass = macro class {
|
||||
|
||||
};
|
||||
|
||||
var definedFields:Array<String> = [];
|
||||
|
||||
//trace(getModuleName(cl));
|
||||
|
||||
var hasNew = false;
|
||||
|
||||
for(_field in [fields.copy(), superFields.copy()])
|
||||
for(f in _field) {
|
||||
if (f == null)
|
||||
continue;
|
||||
if (f.name == "new") {
|
||||
hasNew = true;
|
||||
switch (f.kind) {
|
||||
case FFun(fn):
|
||||
var constructor:Field = buildConstructor(fn.args);
|
||||
|
||||
shadowClass.fields.push(constructor);
|
||||
definedFields.push(f.name);
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (f.name.startsWith(FUNC_PREFIX))
|
||||
continue;
|
||||
if (f.access.contains(ADynamic) || f.access.contains(AStatic) || f.access.contains(AExtern) || f.access.contains(AInline) || f.access.contains(AFinal))
|
||||
continue;
|
||||
|
||||
if(f.name == "hget" || f.name == "hset") continue; // sorry, no overwriting the hget and hset in custom classes, yet
|
||||
if(definedFields.contains(f.name)) continue; // no duplicate fields
|
||||
|
||||
for(m in f.meta)
|
||||
if (unallowedMetas.contains(m.name))
|
||||
continue;
|
||||
|
||||
switch(f.kind) {
|
||||
case FFun(fun):
|
||||
if (fun == null)
|
||||
continue;
|
||||
if (fun.params != null && fun.params.length > 0) // TODO: Support for this maybe?
|
||||
continue;
|
||||
|
||||
if(fun.params == null)
|
||||
fun.params = [];
|
||||
|
||||
var overrideExpr:Expr;
|
||||
var returns:Bool = !fun.ret.match(TPath({name: "Void"}));
|
||||
|
||||
var name = f.name;
|
||||
|
||||
var arguments = fun.args == null ? [] : [for(a in fun.args) macro $i{a.name}];
|
||||
|
||||
if (returns) {
|
||||
overrideExpr = macro {
|
||||
var name:String = $v{name};
|
||||
/*
|
||||
if (__custom__variables != null) {
|
||||
if(__custom__variables.exists(name)) {
|
||||
var v:Dynamic = null;
|
||||
if (Reflect.isFunction(v = __custom__variables.get(name))) {
|
||||
return v($a{arguments});
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (__customClass != null && @:privateAccess __customClass.hasFunction(name)) {
|
||||
return __customClass.callFunction(name, [$a{arguments}]);
|
||||
}
|
||||
|
||||
return super.$name($a{arguments});
|
||||
};
|
||||
} else {
|
||||
overrideExpr = macro {
|
||||
var name:String = $v{name};
|
||||
/*
|
||||
if (__custom__variables != null) {
|
||||
if(__custom__variables.exists(name)) {
|
||||
var v:Dynamic = null;
|
||||
if (Reflect.isFunction(v = __custom__variables.get(name))) {
|
||||
v($a{arguments});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (__customClass != null && @:privateAccess __customClass.hasFunction(name)) {
|
||||
__customClass.callFunction(name, [$a{arguments}]);
|
||||
return;
|
||||
}
|
||||
|
||||
super.$name($a{arguments});
|
||||
};
|
||||
}
|
||||
|
||||
var superFuncExpr:Expr = returns ? {
|
||||
macro return super.$name($a{arguments});
|
||||
} : {
|
||||
macro super.$name($a{arguments});
|
||||
};
|
||||
|
||||
var func:Function = {
|
||||
ret: fun.ret,
|
||||
params: fun.params.copy(),
|
||||
expr: overrideExpr,
|
||||
args: fun.args.copy()
|
||||
};
|
||||
|
||||
var overrideField:Field = {
|
||||
name: f.name,
|
||||
access: f.access.copy(),
|
||||
kind: FFun(func),
|
||||
pos: Context.currentPos(),
|
||||
doc: f.doc,
|
||||
meta: f.meta.copy()
|
||||
};
|
||||
|
||||
if (!overrideField.access.contains(AOverride))
|
||||
overrideField.access.push(AOverride);
|
||||
|
||||
var superField:Field = {
|
||||
name: '$FUNC_PREFIX${f.name}',
|
||||
pos: Context.currentPos(),
|
||||
kind: FFun({
|
||||
ret: fun.ret,
|
||||
params: fun.params.copy(),
|
||||
expr: superFuncExpr,
|
||||
args: fun.args.copy()
|
||||
}),
|
||||
access: f.access.copy()
|
||||
};
|
||||
if (superField.access.contains(AOverride))
|
||||
superField.access.remove(AOverride);
|
||||
shadowClass.fields.push(overrideField);
|
||||
shadowClass.fields.push(superField);
|
||||
definedFields.push(f.name);
|
||||
default:
|
||||
// fuck off >:(
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var totalFields = definedFields.length;
|
||||
|
||||
if(totalFields == 0 && !hasNew) {
|
||||
//Sys.println(cl.pack.join(".") + "." + cl.name + ", " + totalFields);
|
||||
return fields;
|
||||
}
|
||||
|
||||
shadowClass.kind = TDClass({
|
||||
pack: cl.pack.copy(),
|
||||
name: cl.name
|
||||
}, [
|
||||
{name: "IHScriptCustomBehaviour", pack: ["hscript"]},
|
||||
{name: "IHScriptCustomClassBehaviour", pack: ["hscript"]}
|
||||
], false, true, false);
|
||||
shadowClass.name = '${cl.name}$CLASS_SUFFIX';
|
||||
var imports = Context.getLocalImports().copy();
|
||||
Utils.setupMetas(shadowClass, imports);
|
||||
Utils.processImport(imports, "hscript.utils.UnsafeReflect", "UnsafeReflect");
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__cachedFields",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(macro: Map<String, Dynamic>),
|
||||
access: [APublic, AStatic]
|
||||
});
|
||||
|
||||
// Adding hscript getters and setters
|
||||
/*
|
||||
shadowClass.fields.push({
|
||||
name: "__interp",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(macro: hscript.Interp),
|
||||
access: [APublic]
|
||||
});
|
||||
*/
|
||||
shadowClass.fields.push({
|
||||
name: "__customClass",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(macro: hscript.customclass.CustomClass),
|
||||
access: [APublic]
|
||||
});
|
||||
/*
|
||||
shadowClass.fields.push({
|
||||
name: "__custom__variables",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(macro: Map<String, Dynamic>),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__allowSetGet",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(macro: Bool, macro true),
|
||||
access: [APublic]
|
||||
});
|
||||
*/
|
||||
shadowClass.fields.push({
|
||||
name: "__real_fields",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(macro: Array<String>),
|
||||
access: [APublic]
|
||||
});
|
||||
/*
|
||||
shadowClass.fields.push({
|
||||
name: "__class__fields",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(macro: Array<String>),
|
||||
access: [APublic]
|
||||
});
|
||||
*/
|
||||
// Unneccessary since the get/set call is already handled.
|
||||
/*
|
||||
shadowClass.fields.push({
|
||||
name: "__callGetter",
|
||||
pos: Context.currentPos(),
|
||||
kind: FFun({
|
||||
ret: macro: Dynamic,
|
||||
params: [],
|
||||
expr: macro {
|
||||
__allowSetGet = false;
|
||||
var v = __custom__variables.get("get_" + name)();
|
||||
__allowSetGet = true;
|
||||
return v;
|
||||
},
|
||||
args: [
|
||||
{
|
||||
name: "name",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: macro: String
|
||||
}
|
||||
]
|
||||
}),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__callSetter",
|
||||
pos: Context.currentPos(),
|
||||
kind: FFun({
|
||||
ret: macro: Dynamic,
|
||||
params: [],
|
||||
expr: macro {
|
||||
__allowSetGet = false;
|
||||
var v = __custom__variables.get("set_" + name)(val);
|
||||
__allowSetGet = true;
|
||||
return v;
|
||||
},
|
||||
args: [
|
||||
{
|
||||
name: "name",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: macro: String
|
||||
},
|
||||
{
|
||||
name: "val",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: macro: Dynamic
|
||||
}
|
||||
]
|
||||
}),
|
||||
access: [APublic]
|
||||
});
|
||||
*/
|
||||
// Todo: make it possible to override
|
||||
if(cl.name == "FunkinShader" || cl.name == "CustomShader" || cl.name == "MultiThreadedScript") {
|
||||
Context.defineModule(cl.module, [shadowClass], imports);
|
||||
return fields;
|
||||
}
|
||||
|
||||
var hasHgetInSuper = false;
|
||||
var hasHsetInSuper = false;
|
||||
|
||||
if(cl.name == "CustomShader") {
|
||||
hasHgetInSuper = hasHsetInSuper = true;
|
||||
}
|
||||
|
||||
// TODO: somehow check the super super class
|
||||
for(_field in [fields.copy(), superFields.copy()])
|
||||
for(f in _field) {
|
||||
if (f.name == "new")
|
||||
continue;
|
||||
if (f.name.startsWith(FUNC_PREFIX))
|
||||
continue;
|
||||
if (f.access.contains(ADynamic) || f.access.contains(AStatic) || f.access.contains(AExtern))
|
||||
continue;
|
||||
|
||||
switch(f.kind) {
|
||||
case FFun(fun):
|
||||
if (fun.params != null && fun.params.length > 0)
|
||||
continue;
|
||||
|
||||
if(!hasHgetInSuper)
|
||||
hasHgetInSuper = f.name == "hget";
|
||||
if(!hasHsetInSuper)
|
||||
hasHsetInSuper = f.name == "hset";
|
||||
|
||||
if(hasHgetInSuper && hasHsetInSuper)
|
||||
break;
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var hgetField = if(hasHgetInSuper) {
|
||||
macro {
|
||||
/*
|
||||
if(__custom__variables != null) {
|
||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
if (__custom__variables.exists(name))
|
||||
return __custom__variables.get(name);
|
||||
}
|
||||
*/
|
||||
return super.hget(name);
|
||||
}
|
||||
} else {
|
||||
macro {
|
||||
/*
|
||||
if(__custom__variables != null) {
|
||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
if (__custom__variables.exists(name))
|
||||
return __custom__variables.get(name);
|
||||
}
|
||||
*/
|
||||
return UnsafeReflect.getProperty(this, name);
|
||||
}
|
||||
}
|
||||
|
||||
var hsetField = if(hasHsetInSuper) {
|
||||
macro {
|
||||
/*
|
||||
if(__custom__variables != null) {
|
||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||
return __callSetter(name, val);
|
||||
if (__custom__variables.exists(name)) {
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
*/
|
||||
if(__real_fields != null && __real_fields.contains(name)) {
|
||||
UnsafeReflect.setProperty(this, name, val);
|
||||
return UnsafeReflect.field(this, name);
|
||||
}
|
||||
return super.hset(name, val);
|
||||
}
|
||||
} else {
|
||||
macro {
|
||||
/*
|
||||
if(__custom__variables != null) {
|
||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||
return __callSetter(name, val);
|
||||
if (__custom__variables.exists(name)) {
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
*/
|
||||
if(__real_fields != null && __real_fields.contains(name)) {
|
||||
UnsafeReflect.setProperty(this, name, val);
|
||||
return UnsafeReflect.field(this, name);
|
||||
}
|
||||
//if(__custom__variables != null) __custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
//if(hasHsetInSuper || hasHgetInSuper) return fields;
|
||||
|
||||
//trace(cl.name);
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "hset",
|
||||
pos: Context.currentPos(),
|
||||
access: hasHsetInSuper ? [AOverride, APublic] : [APublic],
|
||||
kind: FFun({
|
||||
ret: macro: Dynamic,
|
||||
params: [],
|
||||
expr: hsetField,
|
||||
args: [
|
||||
{
|
||||
name: "name",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: macro: String
|
||||
},
|
||||
{
|
||||
name: "val",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: macro: Dynamic
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "hget",
|
||||
pos: Context.currentPos(),
|
||||
access: hasHgetInSuper ? [AOverride, APublic] : [APublic],
|
||||
kind: FFun({
|
||||
ret: macro: Dynamic,
|
||||
params: [],
|
||||
expr: hgetField,
|
||||
args: [
|
||||
{
|
||||
name: "name",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: macro: String
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
/*var p = new Printer();
|
||||
var aa = p.printTypeDefinition(shadowClass);
|
||||
if(aa.length < 5024)
|
||||
trace(aa);
|
||||
if(aa.indexOf("pack") >= 0)
|
||||
if(cl.name == "FunkinShader")*/
|
||||
|
||||
Context.defineModule(cl.module, [shadowClass], imports);
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
static function buildConstructor(constArgs:Array<FunctionArg>):Field {
|
||||
var superCallArgs:Array<Expr> = [for (arg in constArgs) macro $i{arg.name}];
|
||||
|
||||
return {
|
||||
name: 'new',
|
||||
access: [APublic],
|
||||
pos: Context.currentPos(),
|
||||
kind: FFun({
|
||||
args: constArgs,
|
||||
expr: macro
|
||||
{
|
||||
// Call the super constructor with appropriate args
|
||||
super($a{superCallArgs});
|
||||
|
||||
if(__cachedFields != null) {
|
||||
for(k => v in __cachedFields) {
|
||||
Reflect.setProperty(this, k, v);
|
||||
trace(k);
|
||||
}
|
||||
__cachedFields = null;
|
||||
}
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
#else
|
||||
class ClassExtendMacro {
|
||||
public var usedClass:Class<Dynamic>;
|
||||
public var className:String;
|
||||
}
|
||||
#end
|
||||
@@ -0,0 +1,529 @@
|
||||
package hscript.macros;
|
||||
|
||||
/*
|
||||
* Copyright (C)2005-2019 Haxe Foundation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Type;
|
||||
import Type as StdType;
|
||||
|
||||
using Lambda;
|
||||
|
||||
/**
|
||||
This class provides some utility methods to work with types. It is
|
||||
best used through 'using haxe.macro.TypeTools' syntax and then provides
|
||||
additional methods on haxe.macro.Type instances.
|
||||
**/
|
||||
#if hl
|
||||
@:hlNative("macro")
|
||||
#end
|
||||
class FixedTypeTools {
|
||||
static function nullable(complexType:ComplexType):ComplexType
|
||||
return macro:Null<$complexType>;
|
||||
|
||||
public static function toField(cf:ClassField):Field {
|
||||
function varAccessToString(va:VarAccess, getOrSet:String):String {
|
||||
return {
|
||||
switch (va) {
|
||||
case AccNormal | AccCtor: "default";
|
||||
case AccNo: "null";
|
||||
case AccNever: "never";
|
||||
case AccResolve: throw "Invalid " + StdType.enumConstructor(cf.type) + " in varAccessToString";
|
||||
case AccCall: getOrSet;
|
||||
case AccInline: "default";
|
||||
case AccRequire(_, _): "default";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var access = cf.isPublic ? [APublic] : [APrivate];
|
||||
if (cf.meta.has(":final")) {
|
||||
access.push(AFinal);
|
||||
}
|
||||
|
||||
if (cf.params.length != 0)
|
||||
throw "Invalid " + StdType.enumConstructor(cf.type) + " has more than 0 params";
|
||||
|
||||
return {
|
||||
name: cf.name,
|
||||
doc: cf.doc,
|
||||
access: access,
|
||||
kind: switch ([cf.kind, cf.type]) {
|
||||
case [FVar(read, write), ret]:
|
||||
FProp(varAccessToString(read, "get"), varAccessToString(write, "set"), toComplexType(ret), null);
|
||||
case [FMethod(_), TFun(args, ret)]:
|
||||
Sys.println("Converting " + cf.name);
|
||||
FFun({
|
||||
args: [
|
||||
for (a in args)
|
||||
{
|
||||
name: a.name,
|
||||
opt: a.opt,
|
||||
type: toComplexType(a.t),
|
||||
}
|
||||
],
|
||||
ret: toComplexType(ret),
|
||||
expr: null,
|
||||
});
|
||||
case [FMethod(_), TLazy(f)]:
|
||||
Sys.println("Converting lazy " + cf.name + " in " + cf.pos);
|
||||
switch(f()) {
|
||||
case TFun(args, ret):
|
||||
FFun({
|
||||
args: [
|
||||
for (a in args)
|
||||
{
|
||||
name: a.name,
|
||||
opt: a.opt,
|
||||
type: toComplexType(a.t),
|
||||
}
|
||||
],
|
||||
ret: toComplexType(ret),
|
||||
expr: null,
|
||||
});
|
||||
default:
|
||||
throw "Invalid " + StdType.enumConstructor(cf.type) + " when converting to Field , " + cf.kind + ", " + cf.type;
|
||||
}
|
||||
default:
|
||||
throw "Invalid " + StdType.enumConstructor(cf.type) + " when converting to Field , " + cf.kind + ", " + cf.type;
|
||||
},
|
||||
pos: cf.pos,
|
||||
meta: cf.meta.get(),
|
||||
}
|
||||
}
|
||||
|
||||
public static function getAccess(cf:ClassField):Array<Access> {
|
||||
var access = cf.isPublic ? [APublic] : [APrivate];
|
||||
if (cf.meta.has(":final") || cf.isFinal) {
|
||||
access.push(AFinal);
|
||||
}
|
||||
switch ([cf.kind, cf.type]) {
|
||||
case [FMethod(kind), TFun(_, _)] | [FMethod(kind), TLazy(_)]:
|
||||
if(kind == MethInline)
|
||||
access.push(AInline);
|
||||
if(kind == MethDynamic)
|
||||
access.push(ADynamic);
|
||||
default:
|
||||
}
|
||||
return access;
|
||||
}
|
||||
|
||||
public static function toSimpleField(cf:ClassField):Field {
|
||||
function varAccessToString(va:VarAccess, getOrSet:String):String {
|
||||
return {
|
||||
switch (va) {
|
||||
case AccNormal | AccCtor: "default";
|
||||
case AccNo: "null";
|
||||
case AccNever: "never";
|
||||
case AccResolve: throw "Invalid " + StdType.enumConstructor(cf.type) + " in varAccessToString";
|
||||
case AccCall: getOrSet;
|
||||
case AccInline: "default";
|
||||
case AccRequire(_, _): "default";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var access = cf.isPublic ? [APublic] : [APrivate];
|
||||
if (cf.meta.has(":final")) {
|
||||
access.push(AFinal);
|
||||
}
|
||||
|
||||
if (cf.params.length != 0)
|
||||
throw "Invalid " + StdType.enumConstructor(cf.type) + " has more than 0 params";
|
||||
|
||||
return {
|
||||
name: cf.name,
|
||||
doc: cf.doc,
|
||||
access: access,
|
||||
kind: switch ([cf.kind, cf.type]) {
|
||||
case [FVar(read, write), ret]:
|
||||
FProp(varAccessToString(read, "get"), varAccessToString(write, "set"), toComplexType(ret), null);
|
||||
case [FMethod(_), TFun(args, ret)]:
|
||||
Sys.println("Converting " + cf.name);
|
||||
FFun({
|
||||
args: [
|
||||
for (a in args)
|
||||
{
|
||||
name: a.name,
|
||||
opt: a.opt,
|
||||
type: null,//toComplexType(a.t),
|
||||
}
|
||||
],
|
||||
ret: null,//toComplexType(ret),
|
||||
expr: null,
|
||||
});
|
||||
case [FMethod(_), TLazy(f)]:
|
||||
Sys.println("Converting lazy " + cf.name + " in " + cf.pos);
|
||||
switch(f()) {
|
||||
case TFun(args, ret):
|
||||
FFun({
|
||||
args: [
|
||||
for (a in args)
|
||||
{
|
||||
name: a.name,
|
||||
opt: a.opt,
|
||||
type: null,//toComplexType(a.t),
|
||||
}
|
||||
],
|
||||
ret: null,//toComplexType(ret),
|
||||
expr: null,
|
||||
});
|
||||
default:
|
||||
throw "Invalid " + StdType.enumConstructor(cf.type) + " when converting to Field , " + cf.kind + ", " + cf.type;
|
||||
}
|
||||
default:
|
||||
throw "Invalid " + StdType.enumConstructor(cf.type) + " when converting to Field , " + cf.kind + ", " + cf.type;
|
||||
},
|
||||
pos: cf.pos,
|
||||
meta: cf.meta.get(),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a syntax-level type corresponding to Type `t`.
|
||||
|
||||
This function is mostly inverse to `ComplexTypeTools.toType`, but may
|
||||
lose some information on types that do not have a corresponding syntax
|
||||
version, such as monomorphs. In these cases, the result is null.
|
||||
|
||||
If `t` is null, an internal exception is thrown.
|
||||
**/
|
||||
public static function toComplexType(type:Null<Type>):Null<ComplexType>
|
||||
return {
|
||||
#if macro
|
||||
Context.toComplexType(type);
|
||||
#else
|
||||
switch (type) {
|
||||
case null:
|
||||
null;
|
||||
case TMono(_.get() => t):
|
||||
t == null ? null : toComplexType(t);
|
||||
case TEnum(_.get() => baseType, params):
|
||||
TPath(toTypePath(baseType, params));
|
||||
case TInst(_.get() => classType, params):
|
||||
switch (classType.kind) {
|
||||
case KTypeParameter(_):
|
||||
TPath({
|
||||
name: classType.name,
|
||||
pack: [],
|
||||
});
|
||||
default:
|
||||
TPath(toTypePath(classType, params));
|
||||
}
|
||||
case TType(_.get() => baseType, params):
|
||||
TPath(toTypePath(baseType, params));
|
||||
case TFun(args, ret):
|
||||
TFunction([for (a in args) a.opt ? nullable(toComplexType(a.t)) : toComplexType(a.t)], toComplexType(ret));
|
||||
case TAnonymous(_.get() => {fields: fields}):
|
||||
TAnonymous([for (cf in fields) toField(cf)]);
|
||||
case TDynamic(t):
|
||||
if (t == null) {
|
||||
macro:Dynamic;
|
||||
} else {
|
||||
var ct = toComplexType(t);
|
||||
macro:Dynamic<$ct>;
|
||||
}
|
||||
case TLazy(f):
|
||||
toComplexType(f());
|
||||
case TAbstract(_.get() => baseType, params):
|
||||
TPath(toTypePath(baseType, params));
|
||||
default:
|
||||
throw "Invalid type";
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
static function toTypeParam(type:Type):TypeParam
|
||||
return {
|
||||
switch (type) {
|
||||
case TInst(_.get() => {kind: KExpr(e)}, _): TPExpr(e);
|
||||
case _: TPType(toComplexType(type));
|
||||
}
|
||||
}
|
||||
|
||||
static function toTypePath(baseType:BaseType, params:Array<Type>):TypePath
|
||||
return {
|
||||
var module = baseType.module;
|
||||
{
|
||||
pack: baseType.pack,
|
||||
name: module.substring(module.lastIndexOf(".") + 1),
|
||||
sub: baseType.name,
|
||||
params: [for (t in params) toTypeParam(t)],
|
||||
}
|
||||
}
|
||||
|
||||
#if macro
|
||||
/**
|
||||
Follows all typedefs of `t` to reach the actual type.
|
||||
|
||||
If `once` is true, this function does not call itself recursively,
|
||||
otherwise it does. This can be useful in cases where intermediate
|
||||
typedefs might be of interest.
|
||||
|
||||
Affected types are monomorphs `TMono` and typedefs `TType(t,pl)`.
|
||||
|
||||
If `t` is null, an internal exception is thrown.
|
||||
|
||||
Usage example with monomorphs:
|
||||
var t = Context.typeof(macro null); // TMono(<mono>)
|
||||
var ts = Context.typeof(macro "foo"); //TInst(String,[])
|
||||
Context.unify(t, ts);
|
||||
trace(t); // TMono(<mono>)
|
||||
trace(t.follow()); //TInst(String,[])
|
||||
|
||||
Usage example with typedefs:
|
||||
var t = Context.typeof(macro ("foo" :MyString)); // typedef MyString = String
|
||||
trace(t); // TType(MyString,[])
|
||||
trace(t.follow()); //TInst(String,[])
|
||||
**/
|
||||
static public inline function follow(t:Type, ?once:Bool):Type
|
||||
return Context.follow(t, once);
|
||||
|
||||
/**
|
||||
Like `follow`, follows all typedefs of `t` to reach the actual type.
|
||||
|
||||
Will however follow also abstracts to their underlying implementation,
|
||||
if they are not a @:coreType abstract
|
||||
|
||||
If `t` is null, an internal exception is thrown.
|
||||
|
||||
Usage example:
|
||||
var t = Context.typeof(macro new Map<String, String>());
|
||||
trace(t); // TAbstract(Map,[TInst(String,[]),TInst(String,[])])
|
||||
trace(t.followWithAbstracts()); // TInst(haxe.ds.StringMap, [TInst(String,[])])
|
||||
**/
|
||||
static public inline function followWithAbstracts(t:Type, once:Bool = false):Type
|
||||
return Context.followWithAbstracts(t, once);
|
||||
|
||||
/**
|
||||
Returns true if `t1` and `t2` unify, false otherwise.
|
||||
**/
|
||||
static public inline function unify(t1:Type, t2:Type):Bool
|
||||
return Context.unify(t1, t2);
|
||||
|
||||
/**
|
||||
Tries to extract the class instance stored inside `t`.
|
||||
|
||||
If `t` is a class instance `TInst(c,pl)`, c is returned.
|
||||
|
||||
If `t` is of a different type, an exception of type String is thrown.
|
||||
|
||||
If `t` is null, the result is null.
|
||||
**/
|
||||
static public function getClass(t:Type)
|
||||
return t == null ? null : switch (follow(t)) {
|
||||
case TInst(c, _): c.get();
|
||||
case _: throw "Class instance expected";
|
||||
}
|
||||
|
||||
/**
|
||||
Tries to extract the enum instance stored inside `t`.
|
||||
|
||||
If `t` is an enum instance `TEnum(e,pl)`, e is returned.
|
||||
|
||||
If `t` is of a different type, an exception of type String is thrown.
|
||||
|
||||
If `t` is null, the result is null.
|
||||
**/
|
||||
static public function getEnum(t:Type)
|
||||
return t == null ? null : switch (follow(t)) {
|
||||
case TEnum(e, _): e.get();
|
||||
case _: throw "Enum instance expected";
|
||||
}
|
||||
|
||||
/**
|
||||
Applies the type parameters `typeParameters` to type `t` with the given
|
||||
types `concreteTypes`.
|
||||
|
||||
This function replaces occurrences of type parameters in `t` if they are
|
||||
part of `typeParameters`. The array index of such a type parameter is
|
||||
then used to lookup the concrete type in `concreteTypes`.
|
||||
|
||||
If `typeParameters.length` is not equal to `concreteTypes.length`, an
|
||||
exception of type `String` is thrown.
|
||||
|
||||
If `typeParameters.length` is 0, `t` is returned unchanged.
|
||||
|
||||
If either argument is `null`, the result is unspecified.
|
||||
**/
|
||||
static public function applyTypeParameters(t:Type, typeParameters:Array<TypeParameter>, concreteTypes:Array<Type>):Type {
|
||||
if (typeParameters.length != concreteTypes.length)
|
||||
throw 'Incompatible arguments: ${typeParameters.length} type parameters and ${concreteTypes.length} concrete types';
|
||||
else if (typeParameters.length == 0)
|
||||
return t;
|
||||
#if (neko || eval)
|
||||
return @:privateAccess Context.load("apply_params", 3)(typeParameters, concreteTypes, t);
|
||||
#else
|
||||
return applyParams(typeParameters, concreteTypes, t);
|
||||
#end
|
||||
}
|
||||
|
||||
#if !neko
|
||||
private static function applyParams(typeParameters:Array<TypeParameter>, concreteTypes:Array<Type>, t:Type):Type {
|
||||
return null;
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
Transforms `t` by calling `f` on each of its subtypes.
|
||||
|
||||
If `t` is a compound type, `f` is called on each of its components.
|
||||
|
||||
Otherwise `t` is returned unchanged.
|
||||
|
||||
The following types are considered compound:
|
||||
- TInst, TEnum, TType and TAbstract with type parameters
|
||||
- TFun
|
||||
- TAnonymous
|
||||
|
||||
If `t` or `f` are null, the result is unspecified.
|
||||
**/
|
||||
static public function map(t:Type, f:Type->Type):Type {
|
||||
return switch (t) {
|
||||
case TMono(tm):
|
||||
switch (tm.get()) {
|
||||
case null: t;
|
||||
case var t: f(t);
|
||||
}
|
||||
case TEnum(_, []) | TInst(_, []) | TType(_, []):
|
||||
t;
|
||||
case TEnum(en, tl):
|
||||
TEnum(en, tl.map(f));
|
||||
case TInst(cl, tl):
|
||||
TInst(cl, tl.map(f));
|
||||
case TType(t2, tl):
|
||||
TType(t2, tl.map(f));
|
||||
case TAbstract(a, tl):
|
||||
TAbstract(a, tl.map(f));
|
||||
case TFun(args, ret):
|
||||
TFun(args.map(function(arg) return {
|
||||
name: arg.name,
|
||||
opt: arg.opt,
|
||||
t: f(arg.t)
|
||||
}), f(ret));
|
||||
case TAnonymous(an):
|
||||
TAnonymous(@:privateAccess Context.load("map_anon_ref", 2)(an, f));
|
||||
case TDynamic(t2):
|
||||
t == t2 ? t : TDynamic(f(t2));
|
||||
case TLazy(ft):
|
||||
var ft = ft();
|
||||
var ft2 = f(ft);
|
||||
ft == ft2 ? t : ft2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Calls function `f` on each component of type `t`.
|
||||
|
||||
If `t` is not a compound type, this operation has no effect.
|
||||
|
||||
The following types are considered compound:
|
||||
- TInst, TEnum, TType and TAbstract with type parameters
|
||||
- TFun
|
||||
- TAnonymous
|
||||
|
||||
If `t` or `f` are null, the result is unspecified.
|
||||
**/
|
||||
static public function iter(t:Type, f:Type->Void):Void {
|
||||
switch (t) {
|
||||
case TMono(tm):
|
||||
var t = tm.get();
|
||||
if (t != null)
|
||||
f(t);
|
||||
case TEnum(_, tl) | TInst(_, tl) | TType(_, tl) | TAbstract(_, tl):
|
||||
for (t in tl)
|
||||
f(t);
|
||||
case TDynamic(t2):
|
||||
if (t != t2)
|
||||
f(t2);
|
||||
case TLazy(ft):
|
||||
f(ft());
|
||||
case TAnonymous(an):
|
||||
for (field in an.get().fields)
|
||||
f(field.type);
|
||||
case TFun(args, ret):
|
||||
for (arg in args)
|
||||
f(arg.t);
|
||||
f(ret);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Converts type `t` to a human-readable String representation.
|
||||
**/
|
||||
static public function toString(t:Type):String {
|
||||
#if (neko || eval)
|
||||
return @:privateAccess Context.load("s_type", 1)(t);
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Changes the name of the variable in the typed expression.
|
||||
**/
|
||||
static public function setVarName(t:TVar, name:String) {
|
||||
@:privateAccess Context.load("set_var_name", 2)(t, name);
|
||||
}
|
||||
|
||||
/**
|
||||
Converts type `t` to `ModuleType`.
|
||||
**/
|
||||
static public function toModuleType(t:Type):ModuleType {
|
||||
#if (neko || eval)
|
||||
return @:privateAccess Context.load("type_to_module_type", 1)(t);
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a type from the `ModuleType` argument.
|
||||
**/
|
||||
static public function fromModuleType(mt:ModuleType):Type {
|
||||
#if (neko || eval)
|
||||
return @:privateAccess Context.load("module_type_to_type", 1)(mt);
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
Resolves the field named `name` on class `c`.
|
||||
|
||||
If `isStatic` is true, the classes' static fields are checked. Otherwise
|
||||
the classes' member fields are checked.
|
||||
|
||||
If the field is found, it is returned. Otherwise if `c` has a super
|
||||
class, `findField` recursively checks that super class. Otherwise null
|
||||
is returned.
|
||||
|
||||
If any argument is null, the result is unspecified.
|
||||
**/
|
||||
static public function findField(c:ClassType, name:String, isStatic:Bool = false):Null<ClassField> {
|
||||
var field = (isStatic ? c.statics : c.fields).get().find(function(field) return field.name == name);
|
||||
return if (field != null) field; else if (c.superClass != null) findField(c.superClass.t.get(), name, isStatic); else null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package hscript.macros;
|
||||
|
||||
#if macro
|
||||
import Type.ValueType;
|
||||
import haxe.macro.Compiler;
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Expr;
|
||||
|
||||
using Lambda;
|
||||
using StringTools;
|
||||
using haxe.macro.Tools;
|
||||
|
||||
/**
|
||||
* Macro used for the `using Class;` keyword
|
||||
*
|
||||
* you can make classes be able to be used specifing the classes/packages on Config.hx!
|
||||
* or implementing the interface `hscript.utils.UsingClass`
|
||||
* ```haxe
|
||||
* public static final ALLOWED_USING = ["my.pack.VeryNiceTools"];
|
||||
* ```
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* ```haxe
|
||||
* package my.pack;
|
||||
* // @:usableEntry() // optional
|
||||
* // @:usableEntry(forceAny) // optional // forces the class to be called with any type
|
||||
* // @:usableEntry(onlyBasic) // optional // only basic types will be allowed
|
||||
* // @:usableEntry(onlyBasic, forceAny) // optional // only basic types will be allowed, and the class will be called with any type
|
||||
* class VeryNiceTools implements hscript.utils.UsingClass {}
|
||||
* ```
|
||||
*
|
||||
* @author NeeEoo
|
||||
* @see https://github.com/pisayesiwsi/hscript-iris/blob/master/crowplexus/iris/macro/UsingMacro.macro.hx
|
||||
**/
|
||||
class UsingMacro {
|
||||
public static inline final USING_PREFIX = "_HX_USING__";
|
||||
|
||||
public static var unallowedMetas:Array<String> = [":noUsing", ":noUse"];
|
||||
|
||||
public static function init() {
|
||||
#if !display
|
||||
if(Context.defined("display")) return;
|
||||
for(apply in Config.ALLOWED_USING) {
|
||||
Compiler.addGlobalMetadata(apply, "@:build(hscript.macros.UsingMacro.build())");
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
public static function build() {
|
||||
var cls: haxe.macro.Type.ClassType = Context.getLocalClass().get();
|
||||
var fields = Context.getBuildFields();
|
||||
|
||||
var key = cls.module;
|
||||
var fkey = cls.module + "." + cls.name;
|
||||
var packName = (cls.pack.length > 0 ? cls.pack.join(".") + "." : "") + cls.name;
|
||||
|
||||
if(cls.meta.get().find(function(m) return m.name == ':usingProcessed') != null) return fields;
|
||||
if (Config.DISALLOW_USING.contains(key) || Config.DISALLOW_USING.contains(fkey) || Config.DISALLOW_USING.contains(packName))
|
||||
return fields;
|
||||
|
||||
var entryField = cls.meta.get().find(function(m) return m.name == ':usableEntry');
|
||||
var hasParams = entryField != null && entryField.params != null;
|
||||
var forceAny = false;
|
||||
var onlyBasic = false;
|
||||
if (hasParams) {
|
||||
for (i in 0...entryField.params.length) {
|
||||
if (entryField.params[i].expr.match(EConst(CIdent("onlyBasic"))))
|
||||
onlyBasic = true;
|
||||
if (entryField.params[i].expr.match(EConst(CIdent("forceAny"))))
|
||||
forceAny = true;
|
||||
}
|
||||
}
|
||||
|
||||
var data: Array<Array<String>> = [];
|
||||
|
||||
for (field in fields) {
|
||||
// functions marked with @:noUsing won't be able to be used by variables
|
||||
// also if you want it to be usable in source, but not in the script, use @:noUse
|
||||
for(m in field.meta)
|
||||
if(unallowedMetas.contains(m.name))
|
||||
continue;
|
||||
|
||||
// It's called "static extensions" for some reason
|
||||
if(!field.access.contains(AStatic))
|
||||
continue;
|
||||
|
||||
switch (field.kind) {
|
||||
default:
|
||||
case FFun(f):
|
||||
if (f.args.length == 0)
|
||||
continue;
|
||||
var arg = f.args[0];
|
||||
if (arg.type == null)
|
||||
continue;
|
||||
var type = arg.type;
|
||||
|
||||
var valueType: String = switch (type) {
|
||||
case TPath({name: "Int", pack: []}):
|
||||
"TInt";
|
||||
case TPath({name: "Float", pack: []}):
|
||||
"TFloat";
|
||||
case TPath({name: "Single", pack: []}):
|
||||
"TFloat";
|
||||
case TPath({name: "String", pack: []}):
|
||||
"TClass(String)";
|
||||
case TPath({name: "Bool", pack: []}):
|
||||
"TBool";
|
||||
case TPath({name: "Array", pack: []}):
|
||||
"TClass(Array)";
|
||||
case TPath({name: "Map", pack: []}):
|
||||
"TClass(haxe.Constraints.IMap)";
|
||||
case TPath({name: "Dynamic", pack: []}):
|
||||
null;
|
||||
case TPath({name: "Class", pack: []}):
|
||||
"TClass(null)"; // this feels wrong
|
||||
case TPath({name: "Enum", pack: []}):
|
||||
"TEnum(null)";
|
||||
// case TPath({name: "Void", pack: []}): "ValueType.TVoid";
|
||||
default:
|
||||
null; // null acts as a wildcard
|
||||
}
|
||||
|
||||
// MIGHT CRASH COMPILATION? YEAH, IT CRASH ON TYPE PARAMETERS
|
||||
if (!onlyBasic && valueType == null) {
|
||||
var rtype:haxe.macro.Type = null;
|
||||
|
||||
try {rtype = type.toType();} catch(e) {}
|
||||
|
||||
if(rtype != null) {
|
||||
switch (rtype) {
|
||||
case TInst(t, []):
|
||||
valueType = "TClass(" + t.toString() + ")";
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (forceAny) {
|
||||
valueType = null;
|
||||
}
|
||||
|
||||
data.push([field.name, valueType]);
|
||||
}
|
||||
}
|
||||
|
||||
if(data.length == 0)
|
||||
return fields;
|
||||
|
||||
fields.push({
|
||||
name: '$USING_PREFIX${packName.replace(".", "_")}',
|
||||
access: [APrivate, AStatic],
|
||||
kind: FVar(macro : Map<String, Type.ValueType>, {
|
||||
var arr: Array<Expr> = [];
|
||||
for (i in data)
|
||||
if (i[1] != null)
|
||||
arr.push(macro $v{i[0]} => ${Context.parse("Type.ValueType." + i[1], Context.currentPos())});
|
||||
else
|
||||
arr.push(macro $v{i[0]} => null);
|
||||
macro $a{arr};
|
||||
}),
|
||||
pos: cls.pos,
|
||||
});
|
||||
|
||||
//var printer = new haxe.macro.Printer();
|
||||
//trace(printer.printField(fields[fields.length - 1]));
|
||||
|
||||
cls.meta.add(':usingProcessed', [], cls.pos);
|
||||
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -0,0 +1,209 @@
|
||||
package hscript.macros;
|
||||
|
||||
#if macro
|
||||
import haxe.macro.Type.ClassType;
|
||||
import Type.ValueType;
|
||||
import haxe.macro.Expr.Function;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Type.MetaAccess;
|
||||
import haxe.macro.Type.FieldKind;
|
||||
import haxe.macro.Type.ClassField;
|
||||
import haxe.macro.Type.VarAccess;
|
||||
import haxe.macro.*;
|
||||
|
||||
using StringTools;
|
||||
|
||||
class Utils {
|
||||
public static function fixStdTypes(type:ComplexType) {
|
||||
switch(type) {
|
||||
case TPath({name: "StdTypes"}):
|
||||
var a:TypePath = type.getParameters()[0];
|
||||
a.name = a.sub;
|
||||
a.sub = null;
|
||||
default:
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public static function setupMetas(shadowClass:TypeDefinition, imports) {
|
||||
shadowClass.meta = [];
|
||||
shadowClass.meta.push({name: ":dox", params: [macro hide], pos: Context.currentPos()});
|
||||
shadowClass.meta.push({name: ":noCompletion", params: [], pos: Context.currentPos()});
|
||||
var module = Context.getModule(Context.getLocalModule());
|
||||
for(t in module) {
|
||||
switch(t) {
|
||||
case TInst(t, params):
|
||||
if (t != null) {
|
||||
var e = t.get();
|
||||
processModule(shadowClass, e.module, e.name);
|
||||
processImport(imports, e.module, e.name);
|
||||
}
|
||||
case TEnum(t, params):
|
||||
if (t != null) {
|
||||
var e = t.get();
|
||||
processModule(shadowClass, e.module, e.name);
|
||||
processImport(imports, e.module, e.name);
|
||||
}
|
||||
case TType(t, params):
|
||||
if (t != null) {
|
||||
var e = t.get();
|
||||
processModule(shadowClass, e.module, e.name);
|
||||
processImport(imports, e.module, e.name);
|
||||
}
|
||||
case TAbstract(t, params):
|
||||
if (t != null) {
|
||||
var e = t.get();
|
||||
processModule(shadowClass, e.module, e.name);
|
||||
processImport(imports, e.module, e.name);
|
||||
}
|
||||
default:
|
||||
// not needed?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function processModule(shadowClass:TypeDefinition, module:String, n:String) {
|
||||
if (n.endsWith("_Impl_"))
|
||||
n = n.substr(0, n.length - 6);
|
||||
if (module.endsWith("_Impl_"))
|
||||
module = module.substr(0, module.length - 6);
|
||||
|
||||
shadowClass.meta.push(
|
||||
{
|
||||
name: ':access',
|
||||
params: [
|
||||
Context.parse(fixModuleName(module.endsWith('.${n}') ? module : '${module}.${n}'), Context.currentPos())
|
||||
],
|
||||
pos: Context.currentPos()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*public static function getModuleName(path:Type) {
|
||||
switch(path) {
|
||||
case TPath(name, pack):// | TDClass(name, pack):
|
||||
var str = "";
|
||||
for(p in pack) {
|
||||
str += p + ".";
|
||||
}
|
||||
str += name;
|
||||
return str;
|
||||
|
||||
default:
|
||||
}
|
||||
return "INVALID";
|
||||
}*/
|
||||
|
||||
public static function fixModuleName(name:String) {
|
||||
return [for(s in name.split(".")) if (s.charAt(0) == "_") s.substr(1) else s].join(".");
|
||||
}
|
||||
public static function processImport(imports:Array<ImportExpr>, module:String, n:String) {
|
||||
if (n.endsWith("_Impl_"))
|
||||
n = n.substr(0, n.length - 6);
|
||||
module = fixModuleName(module);
|
||||
if (module.endsWith("_Impl_"))
|
||||
module = module.substr(0, module.length - 6);
|
||||
|
||||
imports.push({
|
||||
path: [for(m in module.split(".")) {
|
||||
name: m,
|
||||
pos: Context.currentPos()
|
||||
}],
|
||||
mode: INormal
|
||||
});
|
||||
}
|
||||
|
||||
public static function cleanExpr(expr:Expr, oldFunc:String, newFunc:String) {
|
||||
if (expr == null) return;
|
||||
if (expr.expr == null) return;
|
||||
switch(expr.expr) {
|
||||
case EConst(c):
|
||||
switch(c) {
|
||||
case CIdent(s):
|
||||
if (s == oldFunc)
|
||||
expr.expr = EConst(CIdent(newFunc));
|
||||
case CString(s, b):
|
||||
if (s == oldFunc)
|
||||
expr.expr = EConst(CString(s, b));
|
||||
default:
|
||||
// nothing
|
||||
}
|
||||
case EField(e, field):
|
||||
if (field == oldFunc && e != null) {
|
||||
switch(e.expr) {
|
||||
case EConst(c):
|
||||
switch(c) {
|
||||
case CIdent(s):
|
||||
if (s == "super")
|
||||
expr.expr = EField(e, newFunc);
|
||||
default:
|
||||
|
||||
}
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
case EParenthesis(e):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case EObjectDecl(fields):
|
||||
for(f in fields) {
|
||||
cleanExpr(f.expr, oldFunc, newFunc);
|
||||
}
|
||||
case EArrayDecl(values):
|
||||
for(a in values) {
|
||||
cleanExpr(a, oldFunc, newFunc);
|
||||
}
|
||||
case ECall(e, params):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case EBlock(exprs):
|
||||
for(e in exprs)
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case EFor(it, expr):
|
||||
cleanExpr(it, oldFunc, newFunc);
|
||||
cleanExpr(expr, oldFunc, newFunc);
|
||||
case EIf(econd, eif, eelse):
|
||||
cleanExpr(econd, oldFunc, newFunc);
|
||||
cleanExpr(eif, oldFunc, newFunc);
|
||||
cleanExpr(eelse, oldFunc, newFunc);
|
||||
case EWhile(econd, e, normalWhile):
|
||||
cleanExpr(econd, oldFunc, newFunc);
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case ECast(e, t):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case ECheckType(e, t):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case ETry(e, catches):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
for(c in catches) {
|
||||
cleanExpr(c.expr, oldFunc, newFunc);
|
||||
}
|
||||
case EThrow(e):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case ETernary(econd, eif, eelse):
|
||||
cleanExpr(econd, oldFunc, newFunc);
|
||||
cleanExpr(eif, oldFunc, newFunc);
|
||||
cleanExpr(eelse, oldFunc, newFunc);
|
||||
case ESwitch(e, cases, edef):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
for(c in cases) {
|
||||
cleanExpr(c.expr, oldFunc, newFunc);
|
||||
}
|
||||
cleanExpr(edef, oldFunc, newFunc);
|
||||
case EReturn(e):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case EIs(e, t):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case EVars(vars):
|
||||
for(v in vars) {
|
||||
cleanExpr(v.expr, oldFunc, newFunc);
|
||||
}
|
||||
case ENew(t, params):
|
||||
for(p in params) {
|
||||
cleanExpr(p, oldFunc, newFunc);
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -0,0 +1,364 @@
|
||||
package hscript.proxy;
|
||||
|
||||
import hscript.customclass.CustomClass;
|
||||
import hscript.customclass.CustomClassDecl;
|
||||
import hscript.Tools.HScriptEnum;
|
||||
import hscript.Tools.EnumValue as HScriptEnumValue;
|
||||
|
||||
abstract OneOfTwo<T1, T2>(Dynamic) from T1 from T2 to T1 to T2 {}
|
||||
|
||||
@:allow(hscript.customclass.CustomClass)
|
||||
@:allow(hscript.customclass.CustomClassDecl)
|
||||
class ProxyType {
|
||||
/**
|
||||
Returns the class of `o`, if `o` is a class instance.
|
||||
|
||||
If `o` is null or of a different type, null is returned.
|
||||
|
||||
In general, type parameter information cannot be obtained at runtime.
|
||||
**/
|
||||
inline static function getClass<T>(o:OneOfTwo<T, CustomClass>):Null<Dynamic> {
|
||||
if(o is CustomClass)
|
||||
@:privateAccess return cast(o, CustomClass).__class;
|
||||
return Type.getClass(cast o);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the enum of enum instance `o`.
|
||||
|
||||
An enum instance is the result of using an enum constructor. Given an
|
||||
`enum Color { Red; }`, `getEnum(Red)` returns `Enum<Color>`.
|
||||
|
||||
If `o` is null, null is returned.
|
||||
|
||||
In general, type parameter information cannot be obtained at runtime.
|
||||
**/
|
||||
inline static function getEnum(o:OneOfTwo<EnumValue, HScriptEnumValue>):Dynamic {
|
||||
// TODO: SUPPORT HSCRIPT ENUMS
|
||||
var isScripted:Bool = Std.isOfType(o, HScriptEnumValue);
|
||||
if(isScripted) {
|
||||
return cast(o, HScriptEnumValue).enumParent;
|
||||
}
|
||||
return Type.getEnum(cast o);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the super-class of class `c`.
|
||||
|
||||
If `c` has no super class, null is returned.
|
||||
|
||||
If `c` is null, the result is unspecified.
|
||||
|
||||
In general, type parameter information cannot be obtained at runtime.
|
||||
**/
|
||||
inline static function getSuperClass(c:OneOfTwo<Class<Dynamic>, CustomClassDecl>):Dynamic {
|
||||
if(c is CustomClassDecl) {
|
||||
return cast(c, CustomClassDecl).superClassDecl;
|
||||
}
|
||||
return Type.getSuperClass(cast c);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the name of class `c`, including its path.
|
||||
|
||||
If `c` is inside a package, the package structure is returned dot-
|
||||
separated, with another dot separating the class name:
|
||||
`pack1.pack2.(...).packN.ClassName`
|
||||
If `c` is a sub-type of a Haxe module, that module is not part of the
|
||||
package structure.
|
||||
|
||||
If `c` has no package, the class name is returned.
|
||||
|
||||
If `c` is null, the result is unspecified.
|
||||
|
||||
The class name does not include any type parameters.
|
||||
**/
|
||||
inline static function getClassName(c:OneOfTwo<Class<Dynamic>, CustomClassDecl>):String {
|
||||
if(c is CustomClassDecl) {
|
||||
var cls:CustomClassDecl = cast c;
|
||||
var name = cls.classDecl.name;
|
||||
var pkg = cls.pkg != null ? '${cls.pkg.join(".")}.' : "";
|
||||
return '$pkg$name';
|
||||
}
|
||||
return Type.getClassName(cast c);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the name of enum `e`, including its path.
|
||||
|
||||
If `e` is inside a package, the package structure is returned dot-
|
||||
separated, with another dot separating the enum name:
|
||||
`pack1.pack2.(...).packN.EnumName`
|
||||
If `e` is a sub-type of a Haxe module, that module is not part of the
|
||||
package structure.
|
||||
|
||||
If `e` has no package, the enum name is returned.
|
||||
|
||||
If `e` is null, the result is unspecified.
|
||||
|
||||
The enum name does not include any type parameters.
|
||||
**/
|
||||
inline static function getEnumName(e:Enum<Dynamic>):String {
|
||||
var isScripted:Bool = Std.isOfType(e, HScriptEnumValue);
|
||||
if (isScripted)
|
||||
return cast(e, HScriptEnumValue).getEnumName();
|
||||
return Type.getEnumName(e);
|
||||
}
|
||||
|
||||
/**
|
||||
Resolves a class by name.
|
||||
|
||||
If `name` is the path of an existing class, that class is returned.
|
||||
|
||||
Otherwise null is returned.
|
||||
|
||||
If `name` is null or the path to a different type, the result is
|
||||
unspecified.
|
||||
|
||||
The class name must not include any type parameters.
|
||||
**/
|
||||
inline static function resolveClass(name:String):Dynamic {
|
||||
if(Interp.customClassExist(name)) {
|
||||
return Interp.getCustomClass(name);
|
||||
}
|
||||
return Type.resolveClass(name);
|
||||
}
|
||||
|
||||
/**
|
||||
Resolves an enum by name.
|
||||
|
||||
If `name` is the path of an existing enum, that enum is returned.
|
||||
|
||||
Otherwise null is returned.
|
||||
|
||||
If `name` is null the result is unspecified.
|
||||
|
||||
If `name` is the path to a different type, null is returned.
|
||||
|
||||
The enum name must not include any type parameters.
|
||||
**/
|
||||
inline static function resolveEnum(name:String):Enum<Dynamic> {
|
||||
return Type.resolveEnum(name);
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an instance of class `cl`, using `args` as arguments to the
|
||||
class constructor.
|
||||
|
||||
This function guarantees that the class constructor is called.
|
||||
|
||||
Default values of constructors arguments are not guaranteed to be
|
||||
taken into account.
|
||||
|
||||
If `cl` or `args` are null, or if the number of elements in `args` does
|
||||
not match the expected number of constructor arguments, or if any
|
||||
argument has an invalid type, or if `cl` has no own constructor, the
|
||||
result is unspecified.
|
||||
|
||||
In particular, default values of constructor arguments are not
|
||||
guaranteed to be taken into account.
|
||||
**/
|
||||
inline static function createInstance<T>(cl:OneOfTwo<Class<T>, CustomClassDecl>, args:Array<Dynamic>):T {
|
||||
// TODO: somehow pass the ogInterp argument without breaking the original functionality
|
||||
if(cl is CustomClassDecl) {
|
||||
return cast new CustomClass(cast cl, args);
|
||||
}
|
||||
return Type.createInstance(cast cl, args);
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an instance of class `cl`.
|
||||
|
||||
This function guarantees that the class constructor is not called.
|
||||
|
||||
If `cl` is null, the result is unspecified.
|
||||
**/
|
||||
inline static function createEmptyInstance<T>(cl:OneOfTwo<Class<T>, CustomClassDecl>):T {
|
||||
// TODO: same as above
|
||||
if(cl is CustomClassDecl)
|
||||
return cast new CustomClass(cast cl, [], null, null, false);
|
||||
return Type.createEmptyInstance(cast cl);
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an instance of enum `e` by calling its constructor `constr` with
|
||||
arguments `params`.
|
||||
|
||||
If `e` or `constr` is null, or if enum `e` has no constructor named
|
||||
`constr`, or if the number of elements in `params` does not match the
|
||||
expected number of constructor arguments, or if any argument has an
|
||||
invalid type, the result is unspecified.
|
||||
**/
|
||||
inline static function createEnum<T>(e:OneOfTwo<Enum<T>, HScriptEnum>, constr:String, ?params:Array<Dynamic>):T {
|
||||
// TODO: SUPPORT HSCRIPT ENUMS
|
||||
var isScripted:Bool = Std.isOfType(e, HScriptEnum);
|
||||
if(isScripted) {
|
||||
var scriptEnum:HScriptEnum = cast e;
|
||||
var enumToCreate = scriptEnum.getEnumValue(constr);
|
||||
if(Reflect.isFunction(enumToCreate)) {
|
||||
return Reflect.callMethod(null, enumToCreate, params);
|
||||
}
|
||||
return enumToCreate;
|
||||
}
|
||||
return Type.createEnum(cast e, constr, params);
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an instance of enum `e` by calling its constructor number
|
||||
`index` with arguments `params`.
|
||||
|
||||
The constructor indices are preserved from Haxe syntax, so the first
|
||||
declared is index 0, the next index 1 etc.
|
||||
|
||||
If `e` or `constr` is null, or if enum `e` has no constructor named
|
||||
`constr`, or if the number of elements in `params` does not match the
|
||||
expected number of constructor arguments, or if any argument has an
|
||||
invalid type, the result is unspecified.
|
||||
**/
|
||||
inline static function createEnumIndex<T>(e:Enum<T>, index:Int, ?params:Array<Dynamic>):T {
|
||||
// TODO: SUPPORT HSCRIPT ENUMS
|
||||
return Type.createEnumIndex(e, index, params);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a list of the instance fields of class `c`, including
|
||||
inherited fields.
|
||||
|
||||
This only includes fields which are known at compile-time. In
|
||||
particular, using `getInstanceFields(getClass(obj))` will not include
|
||||
any fields which were added to `obj` at runtime.
|
||||
|
||||
The order of the fields in the returned Array is unspecified.
|
||||
|
||||
If `c` is null, the result is unspecified.
|
||||
**/
|
||||
inline static function getInstanceFields(c:OneOfTwo<Class<Dynamic>, CustomClassDecl>):Array<String> {
|
||||
if(c is CustomClassDecl) {
|
||||
var cls:CustomClassDecl = cast c;
|
||||
return [for(f in Tools.getClassDeclFields(cls.classDecl)) f.name];
|
||||
}
|
||||
return Type.getInstanceFields(cast c);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a list of static fields of class `c`.
|
||||
|
||||
This does not include static fields of parent classes.
|
||||
|
||||
The order of the fields in the returned Array is unspecified.
|
||||
|
||||
If `c` is null, the result is unspecified.
|
||||
**/
|
||||
inline static function getClassFields(c:OneOfTwo<Class<Dynamic>, CustomClassDecl>):Array<String> {
|
||||
if(c is CustomClassDecl) {
|
||||
var cls:CustomClassDecl = cast c;
|
||||
return [for(f in Tools.getClassDeclFields(cls.classDecl, true)) f.name];
|
||||
}
|
||||
return Type.getClassFields(cast c);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a list of the names of all constructors of enum `e`.
|
||||
|
||||
The order of the constructor names in the returned Array is preserved
|
||||
from the original syntax.
|
||||
|
||||
If `e` is null, the result is unspecified.
|
||||
**/
|
||||
inline static function getEnumConstructs(e:OneOfTwo<Enum<Dynamic>, HScriptEnum>):Array<String> {
|
||||
// TODO: SUPPORT HSCRIPT ENUMS
|
||||
return Type.getEnumConstructs(cast e);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the runtime type of value `v`.
|
||||
|
||||
The result corresponds to the type `v` has at runtime, which may vary
|
||||
per platform. Assumptions regarding this should be minimized to avoid
|
||||
surprises.
|
||||
**/
|
||||
inline static function typeof(v:Dynamic):Type.ValueType {
|
||||
return Type.typeof(v);
|
||||
}
|
||||
|
||||
/**
|
||||
Recursively compares two enum instances `a` and `b` by value.
|
||||
|
||||
Unlike `a == b`, this function performs a deep equality check on the
|
||||
arguments of the constructors, if exists.
|
||||
|
||||
If `a` or `b` are null, the result is unspecified.
|
||||
**/
|
||||
inline static function enumEq(a:Dynamic, b:Dynamic):Bool {
|
||||
var isScripted:Bool = Std.isOfType(a, HScriptEnumValue) && Std.isOfType(b, HScriptEnumValue);
|
||||
if (isScripted)
|
||||
return cast(a, HScriptEnumValue).compare(cast(b, HScriptEnumValue));
|
||||
return Type.enumEq(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the constructor name of enum instance `e`.
|
||||
|
||||
The result String does not contain any constructor arguments.
|
||||
|
||||
If `e` is null, the result is unspecified.
|
||||
**/
|
||||
inline static function enumConstructor(e:Dynamic):String {
|
||||
if (Std.isOfType(e, HScriptEnumValue))
|
||||
return cast(e, HScriptEnumValue).name;
|
||||
return Type.enumConstructor(e);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a list of the constructor arguments of enum instance `e`.
|
||||
|
||||
If `e` has no arguments, the result is [].
|
||||
|
||||
Otherwise the result are the values that were used as arguments to `e`,
|
||||
in the order of their declaration.
|
||||
|
||||
If `e` is null, the result is unspecified.
|
||||
**/
|
||||
inline static function enumParameters(e:Dynamic):Array<Dynamic> {
|
||||
if (Std.isOfType(e, HScriptEnumValue))
|
||||
return cast(e, HScriptEnumValue).args;
|
||||
return Type.enumParameters(e);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the index of enum instance `e`.
|
||||
|
||||
This corresponds to the original syntactic position of `e`. The index of
|
||||
the first declared constructor is 0, the next one is 1 etc.
|
||||
|
||||
If `e` is null, the result is unspecified.
|
||||
**/
|
||||
inline static function enumIndex(e:Dynamic):Int {
|
||||
if (Std.isOfType(e, HScriptEnumValue))
|
||||
return cast(e, HScriptEnumValue).index;
|
||||
return Type.enumIndex(e);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a list of all constructors of enum `e` that require no
|
||||
arguments.
|
||||
|
||||
This may return the empty Array `[]` if all constructors of `e` require
|
||||
arguments.
|
||||
|
||||
Otherwise an instance of `e` constructed through each of its non-
|
||||
argument constructors is returned, in the order of the constructor
|
||||
declaration.
|
||||
|
||||
If `e` is null, the result is unspecified.
|
||||
**/
|
||||
inline static function allEnums<T>(e:OneOfTwo<Enum<T>, HScriptEnum>):Array<T> {
|
||||
// TODO: SUPPORT HSCRIPT ENUMS
|
||||
var isScripted:Bool = Std.isOfType(e, HScriptEnum);
|
||||
if(isScripted) {
|
||||
var hEnum:HScriptEnum = cast e;
|
||||
return [for(en in Reflect.fields(hEnum.enumValues)) Reflect.field(hEnum, en)];
|
||||
}
|
||||
return Type.allEnums(cast e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package hscript.utils;
|
||||
|
||||
#if cpp
|
||||
import cpp.ObjectType;
|
||||
#end
|
||||
|
||||
@:analyzer(ignore)
|
||||
class UnsafeReflect {
|
||||
public #if !cpp inline #end static function hasField(o:Dynamic, field:String):Bool {
|
||||
#if cpp
|
||||
untyped {
|
||||
return o.__HasField(field);
|
||||
}
|
||||
#else
|
||||
return Reflect.hasField(o, field);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function field(o:Dynamic, field:String):Dynamic {
|
||||
#if cpp
|
||||
untyped {
|
||||
return o.__Field(field, untyped __cpp__("::hx::paccNever"));
|
||||
}
|
||||
#else
|
||||
return Reflect.field(o, field);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function setField(o:Dynamic, field:String, value:Dynamic):Void {
|
||||
#if cpp
|
||||
untyped {
|
||||
o.__SetField(field, value, untyped __cpp__("::hx::paccNever"));
|
||||
}
|
||||
#else
|
||||
return Reflect.setField(o, field, value);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function getProperty(o:Dynamic, field:String):Dynamic {
|
||||
#if cpp
|
||||
untyped {
|
||||
return o.__Field(field, untyped __cpp__("::hx::paccAlways"));
|
||||
}
|
||||
#else
|
||||
return Reflect.getProperty(o, field);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function setProperty(o:Dynamic, field:String, value:Dynamic):Void {
|
||||
#if cpp
|
||||
untyped {
|
||||
o.__SetField(field, value, untyped __cpp__("::hx::paccAlways"));
|
||||
}
|
||||
#else
|
||||
Reflect.setProperty(o, field, value);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function callFieldUnsafe(o:Dynamic, field:String, args:Array<Dynamic>):Dynamic {
|
||||
#if cpp
|
||||
untyped {
|
||||
var func:Dynamic = o.__Field(field, untyped __cpp__("::hx::paccDynamic"));
|
||||
untyped func.__SetThis(o);
|
||||
return untyped func.__Run(args);
|
||||
}
|
||||
#else
|
||||
return Reflect.callMethod(o, Reflect.field(o, field), args);
|
||||
#end
|
||||
}
|
||||
|
||||
public inline static function callMethod(o:Dynamic, func:haxe.Constraints.Function, args:Array<Dynamic>):Dynamic {
|
||||
return Reflect.callMethod(o, func, args);
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function callMethodSafe(o:Dynamic, func:haxe.Constraints.Function, args:Array<Dynamic>):Dynamic {
|
||||
#if cpp
|
||||
untyped {
|
||||
if (func == null)
|
||||
throw cpp.ErrorConstants.nullFunctionPointer;
|
||||
untyped func.__SetThis(o);
|
||||
return untyped func.__Run(args);
|
||||
}
|
||||
#else
|
||||
return Reflect.callMethod(o, func, args);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function callMethodUnsafe(o:Dynamic, func:haxe.Constraints.Function, args:Array<Dynamic>):Dynamic {
|
||||
#if cpp
|
||||
untyped {
|
||||
untyped func.__SetThis(o);
|
||||
return untyped func.__Run(args);
|
||||
}
|
||||
#else
|
||||
return Reflect.callMethod(o, func, args);
|
||||
#end
|
||||
}
|
||||
|
||||
public inline static function fields(o:Dynamic):Array<String>
|
||||
return Reflect.fields(o);
|
||||
/*untyped {
|
||||
if (o == null)
|
||||
return new Array();
|
||||
var a:Array<String> = [];
|
||||
o.__GetFields(a);
|
||||
return a;
|
||||
}*/
|
||||
|
||||
public #if !cpp inline #end static function isFunction(f:Dynamic):Bool
|
||||
#if cpp
|
||||
untyped {
|
||||
return f.__GetType() == ObjectType.vtFunction;
|
||||
}
|
||||
#else
|
||||
return Reflect.isFunction(f);
|
||||
#end
|
||||
|
||||
public inline static function compare<T>(a:T, b:T):Int {
|
||||
return Reflect.compare(a, b);
|
||||
//return (a == b) ? 0 : (((a : Dynamic) > (b : Dynamic)) ? 1 : -1);
|
||||
}
|
||||
|
||||
public inline static function compareMethods(f1:Dynamic, f2:Dynamic):Bool {
|
||||
return Reflect.compareMethods(f1, f2);
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function isObject(v:Dynamic):Bool {
|
||||
#if cpp
|
||||
untyped {
|
||||
var t:Int = v.__GetType();
|
||||
return t == ObjectType.vtObject || t == ObjectType.vtClass || t == ObjectType.vtString || t == ObjectType.vtArray;
|
||||
}
|
||||
#else
|
||||
return Reflect.isObject(v);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function isEnumValue(v:Dynamic):Bool {
|
||||
#if cpp
|
||||
untyped {
|
||||
return v.__GetType() == ObjectType.vtEnum;
|
||||
}
|
||||
#else
|
||||
return Reflect.isEnumValue(v);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function deleteField(o:Dynamic, field:String):Bool {
|
||||
#if cpp
|
||||
untyped {
|
||||
return untyped __global__.__hxcpp_anon_remove(o, field);
|
||||
}
|
||||
#else
|
||||
return Reflect.deleteField(o, field);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function copy<T>(o:Null<T>):Null<T> {
|
||||
#if cpp
|
||||
if (o == null)
|
||||
return null;
|
||||
var t:Int = untyped o.__GetType();
|
||||
if (t == ObjectType.vtString)
|
||||
return o;
|
||||
if (t == ObjectType.vtArray)
|
||||
return untyped o.__Field("copy", untyped __cpp__("::hx::paccDynamic"))();
|
||||
var o2:Dynamic = {};
|
||||
for (f in UnsafeReflect.fields(o))
|
||||
UnsafeReflect.setField(o2, f, UnsafeReflect.field(o, f));
|
||||
return o2;
|
||||
#else
|
||||
return Reflect.copy(o);
|
||||
#end
|
||||
}
|
||||
|
||||
@:overload(function(f:Array<Dynamic>->Void):Dynamic {})
|
||||
public static function makeVarArgs(f:Array<Dynamic>->Dynamic):Dynamic {
|
||||
#if cpp
|
||||
return untyped __global__.__hxcpp_create_var_args(f);
|
||||
#else
|
||||
return inline Reflect.makeVarArgs(f);
|
||||
#end
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package hscript.utils;
|
||||
|
||||
/**
|
||||
* This is used to mark classes that can be used with the `using` keyword.
|
||||
* You can also add @:usableEntry to your class.
|
||||
* If you wanna force the class to be called with any type, you can add @:usableEntry(forceAny)
|
||||
**/
|
||||
@:autoBuild(hscript.macros.UsingMacro.build())
|
||||
interface UsingClass {}
|
||||
@@ -0,0 +1,17 @@
|
||||
package hscript.utils;
|
||||
|
||||
typedef UsingCall = (o: Dynamic, f: String, args: Array<Dynamic>) -> Dynamic;
|
||||
|
||||
/**
|
||||
* Code based on Using system from "hscript-iris"
|
||||
* @see https://github.com/pisayesiwsi/hscript-iris/blob/master/crowplexus/iris/utils/UsingEntry.hx
|
||||
*/
|
||||
class UsingEntry {
|
||||
public var name: String;
|
||||
public var call: UsingCall;
|
||||
|
||||
public function new(name: String, call: UsingCall) {
|
||||
this.name = name;
|
||||
this.call = call;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package hscript.utils;
|
||||
|
||||
import hscript.utils.UsingEntry.UsingCall;
|
||||
import StringTools;
|
||||
import Lambda;
|
||||
|
||||
class UsingHandler {
|
||||
public static var usingEntries:Array<UsingEntry> = [
|
||||
new UsingEntry("StringTools", function(o: Dynamic, f: String, args: Array<Dynamic>): Dynamic {
|
||||
if (f == "isEof") // has @:noUsing
|
||||
return null;
|
||||
switch (Type.typeof(o)) {
|
||||
case TInt if (f == "hex"):
|
||||
return StringTools.hex(o, args[0]);
|
||||
case TClass(String):
|
||||
if (Reflect.hasField(StringTools, f)) {
|
||||
var field = Reflect.field(StringTools, f);
|
||||
if (Reflect.isFunction(field)) {
|
||||
return Reflect.callMethod(StringTools, field, [o].concat(args));
|
||||
}
|
||||
}
|
||||
default:
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
new UsingEntry("Lambda", function(o: Dynamic, f: String, args: Array<Dynamic>): Dynamic {
|
||||
if (Tools.isIterable(o)) {
|
||||
// TODO: Check if the values are Iterable<T>
|
||||
if (Reflect.hasField(Lambda, f)) {
|
||||
var field = Reflect.field(Lambda, f);
|
||||
if (Reflect.isFunction(field)) {
|
||||
return Reflect.callMethod(Lambda, field, [o].concat(args));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
];
|
||||
|
||||
public static function registerUsingGlobal(name: String, call:UsingCall):UsingEntry {
|
||||
var entry = new UsingEntry(name, call);
|
||||
usingEntries.push(entry);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
@echo off
|
||||
set PATH="C:\Program Files\7-Zip";%PATH%
|
||||
rm -rf release
|
||||
mkdir release
|
||||
cp haxelib.json README.md extraParams.hxml release
|
||||
|
||||
Reference in New Issue
Block a user