Skip to content

Commit

Permalink
🦄 refactor: Resign ast 6
Browse files Browse the repository at this point in the history
  • Loading branch information
caoccao committed Nov 28, 2024
1 parent 07a03a0 commit 83946e8
Show file tree
Hide file tree
Showing 17 changed files with 672 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/main/java/com/caoccao/javet/buddy/ts2java/Ts2Java.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public void transpile() throws Swc4jCoreException {
Swc4jParseOutput output = swc4j.parse(getTsCode(), swc4jParseOptions);
Swc4jAstModule module = (Swc4jAstModule) output.getProgram();
if (module == null) {
throw new Ts2JavaException("The TypeScript code must be a script, not a module.");
throw new Ts2JavaException("The TypeScript code must be a module, not a script.");
}
if (module.getBody().isEmpty()) {
throw new Ts2JavaException("The TypeScript code must contain at least one statement.");
Expand Down
96 changes: 96 additions & 0 deletions src/main/java/com/caoccao/javet/buddy/ts2java/Ts2JavaX.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2024. caoccao.com Sam Cao
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.caoccao.javet.buddy.ts2java;

import com.caoccao.javet.buddy.ts2java.ast.memo.Ts2JavaMemoDynamicType;
import com.caoccao.javet.buddy.ts2java.ast.stmt.Ts2JavaAstClassDecl;
import com.caoccao.javet.buddy.ts2java.exceptions.Ts2JavaException;
import com.caoccao.javet.swc4j.Swc4j;
import com.caoccao.javet.swc4j.ast.program.Swc4jAstModule;
import com.caoccao.javet.swc4j.ast.stmt.Swc4jAstClassDecl;
import com.caoccao.javet.swc4j.enums.Swc4jMediaType;
import com.caoccao.javet.swc4j.enums.Swc4jParseMode;
import com.caoccao.javet.swc4j.exceptions.Swc4jCoreException;
import com.caoccao.javet.swc4j.options.Swc4jParseOptions;
import com.caoccao.javet.swc4j.outputs.Swc4jParseOutput;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class Ts2JavaX {
protected final static Swc4j swc4j = new Swc4j();
protected final static Swc4jParseOptions swc4jParseOptions = new Swc4jParseOptions()
.setMediaType(Swc4jMediaType.TypeScript)
.setParseMode(Swc4jParseMode.Module)
.setCaptureAst(true);
protected final String packageName;
protected final String tsCode;
protected List<Class<?>> classes;

public Ts2JavaX(String packageName, String tsCode) {
classes = new ArrayList<>();
this.packageName = packageName;
this.tsCode = Objects.requireNonNull(tsCode);
}

public List<Class<?>> getClasses() {
return classes;
}

public String getPackageName() {
return packageName;
}

public String getTsCode() {
return tsCode;
}

public void transpile() throws Swc4jCoreException {
classes.clear();
Swc4jParseOutput output = swc4j.parse(getTsCode(), swc4jParseOptions);
Swc4jAstModule module = (Swc4jAstModule) output.getProgram();
if (module == null) {
throw new Ts2JavaException("The TypeScript code must be a module, not a script.");
}
if (module.getBody().isEmpty()) {
throw new Ts2JavaException("The TypeScript code must contain at least one statement.");
}
List<Swc4jAstClassDecl> classDecls = module.getBody().stream()
.filter(ast -> ast instanceof Swc4jAstClassDecl)
.map(ast -> (Swc4jAstClassDecl) ast)
.collect(Collectors.toList());
if (classDecls.isEmpty()) {
throw new Ts2JavaException("There must be at least one class declaration in the TypeScript code.");
}
for (Swc4jAstClassDecl classDecl : classDecls) {
DynamicType.Builder<?> builder = new ByteBuddy()
.subclass(Object.class, ConstructorStrategy.Default.DEFAULT_CONSTRUCTOR);
Ts2JavaMemoDynamicType memo = new Ts2JavaMemoDynamicType(builder);
Ts2JavaAstClassDecl ast = new Ts2JavaAstClassDecl(null, classDecl, memo, getPackageName());
ast.compile();
builder = memo.getBuilder();
try (DynamicType.Unloaded<?> unloadedType = builder.make()) {
classes.add(unloadedType.load(getClass().getClassLoader()).getLoaded());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@

import com.caoccao.javet.buddy.ts2java.ast.interfaces.ITs2JavaAst;
import com.caoccao.javet.buddy.ts2java.ast.memo.Ts2JavaMemo;
import com.caoccao.javet.buddy.ts2java.ast.memo.Ts2JavaMemoFunction;
import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAst;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.jar.asm.Label;
import net.bytebuddy.jar.asm.MethodVisitor;

import java.util.Objects;

Expand All @@ -27,11 +31,29 @@ public abstract class BaseTs2JavaAst<AST extends ISwc4jAst, Memo extends Ts2Java
protected AST ast;
protected Memo memo;
protected ITs2JavaAst<?, ?> parent;
protected TypeDescription type;

public BaseTs2JavaAst(ITs2JavaAst<?, ?> parent, AST ast, Memo memo) {
this.ast = Objects.requireNonNull(ast);
this.memo = Objects.requireNonNull(memo);
this.parent = parent;
type = TypeDescription.ForLoadedType.of(void.class);
}

protected static Size mergeSize(Size... sizes) {
int sizeImpact = 0;
int maximalSize = 0;
for (Size size : sizes) {
sizeImpact += size.getSizeImpact();
maximalSize = Math.max(maximalSize, size.getMaximalSize());
}
return new Size(sizeImpact, maximalSize);
}

protected static Size mergeSize(Size leftSize, Size rightSize) {
return new Size(
leftSize.getSizeImpact() + rightSize.getSizeImpact(),
Math.max(leftSize.getMaximalSize(), rightSize.getMaximalSize()));
}

@Override
Expand All @@ -47,4 +69,22 @@ public Memo getMemo() {
public ITs2JavaAst<?, ?> getParent() {
return parent;
}

@Override
public TypeDescription getType() {
return type;
}

protected void visitLineNumber(MethodVisitor methodVisitor) {
if (memo instanceof Ts2JavaMemoFunction) {
Ts2JavaMemoFunction memoFunction = (Ts2JavaMemoFunction) memo;
final int lineNumber = ast.getSpan().getLine();
if (memoFunction.getLineNumber() < lineNumber) {
Label label = new Label();
methodVisitor.visitLabel(label);
methodVisitor.visitLineNumber(lineNumber, label);
memoFunction.setLineNumber(lineNumber);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,26 @@
package com.caoccao.javet.buddy.ts2java.ast.clazz;

import com.caoccao.javet.buddy.ts2java.ast.BaseTs2JavaAst;
import com.caoccao.javet.buddy.ts2java.ast.Ts2JavaAstAccessibility;
import com.caoccao.javet.buddy.ts2java.ast.Ts2JavaAstParam;
import com.caoccao.javet.buddy.ts2java.ast.Ts2JavaAstTsTypeAnn;
import com.caoccao.javet.buddy.ts2java.ast.interfaces.ITs2JavaAst;
import com.caoccao.javet.buddy.ts2java.ast.memo.Ts2JavaMemoDynamicType;
import com.caoccao.javet.buddy.ts2java.ast.memo.Ts2JavaMemoFunction;
import com.caoccao.javet.buddy.ts2java.ast.stmt.Ts2JavaAstBlockStmt;
import com.caoccao.javet.swc4j.ast.clazz.Swc4jAstFunction;
import com.caoccao.javet.swc4j.ast.enums.Swc4jAstAccessibility;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.description.type.TypeList;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.jar.asm.MethodVisitor;

import java.util.Optional;

public class Ts2JavaAstFunction
extends BaseTs2JavaAst<Swc4jAstFunction, Ts2JavaMemoDynamicType> {
protected final boolean _static;
protected final Swc4jAstAccessibility accessibility;
protected final Optional<Ts2JavaAstBlockStmt> body;
protected final Ts2JavaMemoFunction memoFunction;
Expand All @@ -42,16 +50,40 @@ public Ts2JavaAstFunction(
boolean _static,
Swc4jAstAccessibility accessibility) {
super(parent, ast, memo);
this._static = _static;
this.accessibility = accessibility;
memoFunction = new Ts2JavaMemoFunction();
memoFunction = new Ts2JavaMemoFunction()
.setStatic(_static);
this.name = name;
body = ast.getBody().map(stmt -> new Ts2JavaAstBlockStmt(this, stmt, memoFunction));
}

@Override
public void compile() {
public Size apply(MethodVisitor methodVisitor, Implementation.Context context) {
if (body.isPresent()) {
return body.get().apply(methodVisitor, context);
}
return Size.ZERO;
}

@Override
public void compile() {
final Visibility visibility = Ts2JavaAstAccessibility.getVisibility(accessibility);
final TypeDescription returnType = ast.getReturnType()
.map(Ts2JavaAstTsTypeAnn::getTypeDescription)
.orElse(TypeDescription.ForLoadedType.of(void.class));
memoFunction.setReturnType(returnType);
ast.getParams().stream()
.map(Ts2JavaAstParam::getLocalVariable)
.forEach(memoFunction::addLocalVariable);
final TypeList parameters = memoFunction.getParameters();
final int initialOffset = memoFunction.getMaxOffset();
memoFunction.pushLexicalScope();
body.ifPresent(Ts2JavaAstBlockStmt::compile);
memo.setBuilder(memo.getBuilder().defineMethod(name, returnType, visibility)
.withParameters(parameters.toArray(new TypeDescription[0]))
.intercept(Implementation.Simple.of(
(implementationTarget, instrumentedMethod) -> new StackManipulation.Simple(this::apply),
memoFunction.getMaxOffset() - initialOffset)));
}

public Swc4jAstAccessibility getAccessibility() {
Expand All @@ -69,8 +101,4 @@ public Ts2JavaMemoFunction getMemoFunction() {
public String getName() {
return name;
}

public boolean isStatic() {
return _static;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright (c) 2024. caoccao.com Sam Cao
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.caoccao.javet.buddy.ts2java.ast.expr.lit;

import com.caoccao.javet.buddy.ts2java.ast.BaseTs2JavaAst;
import com.caoccao.javet.buddy.ts2java.ast.interfaces.ITs2JavaAst;
import com.caoccao.javet.buddy.ts2java.ast.interfaces.ITs2JavaAstLit;
import com.caoccao.javet.buddy.ts2java.ast.interfaces.ITs2JavaAstPropName;
import com.caoccao.javet.buddy.ts2java.ast.interfaces.ITs2JavaAstTsLit;
import com.caoccao.javet.buddy.ts2java.ast.memo.Ts2JavaMemoFunction;
import com.caoccao.javet.buddy.ts2java.exceptions.Ts2JavaAstException;
import com.caoccao.javet.swc4j.ast.expr.lit.Swc4jAstNumber;
import com.caoccao.javet.utils.SimpleFreeMarkerFormat;
import com.caoccao.javet.utils.SimpleMap;
import com.caoccao.javet.utils.StringUtils;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.implementation.bytecode.constant.DoubleConstant;
import net.bytebuddy.implementation.bytecode.constant.FloatConstant;
import net.bytebuddy.implementation.bytecode.constant.IntegerConstant;
import net.bytebuddy.implementation.bytecode.constant.LongConstant;
import net.bytebuddy.jar.asm.MethodVisitor;

public class Ts2JavaAstNumber
extends BaseTs2JavaAst<Swc4jAstNumber, Ts2JavaMemoFunction>
implements ITs2JavaAstLit<Swc4jAstNumber, Ts2JavaMemoFunction>,
ITs2JavaAstPropName<Swc4jAstNumber, Ts2JavaMemoFunction>,
ITs2JavaAstTsLit<Swc4jAstNumber, Ts2JavaMemoFunction> {
public Ts2JavaAstNumber(
ITs2JavaAst<?, ?> parent,
Swc4jAstNumber ast,
TypeDescription type,
Ts2JavaMemoFunction memo) {
super(parent, ast, memo);
this.type = type;
}

@Override
public Size apply(MethodVisitor methodVisitor, Implementation.Context context) {
visitLineNumber(methodVisitor);
StackManipulation stackManipulation;
final boolean isNegative = ast.getMinusCount() % 2 == 1;
if (type.represents(int.class) || type.represents(short.class) || type.represents(byte.class)) {
stackManipulation = IntegerConstant.forValue(isNegative ? -ast.asInt() : ast.asInt());
} else if (type.represents(long.class)) {
stackManipulation = LongConstant.forValue(isNegative ? -ast.asLong() : ast.asLong());
} else if (type.represents(float.class)) {
stackManipulation = FloatConstant.forValue(isNegative ? -ast.asFloat() : ast.asFloat());
} else if (type.represents(double.class)) {
stackManipulation = DoubleConstant.forValue(isNegative ? -ast.asDouble() : ast.asDouble());
} else {
throw new Ts2JavaAstException(
ast,
SimpleFreeMarkerFormat.format("Number type ${type} is not supported.",
SimpleMap.of("type", type.getName())));
}
return stackManipulation.apply(methodVisitor, context);
}

@Override
public void compile() {
if (type == null) {
type = ast.getRaw()
.map(raw -> {
if (StringUtils.isNotEmpty(raw)) {
if (raw.contains(".")) {
return TypeDescription.ForLoadedType.of(double.class);
} else {
long value = Long.parseLong(raw);
if (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) {
return TypeDescription.ForLoadedType.of(int.class);
} else {
return TypeDescription.ForLoadedType.of(long.class);
}
}
} else {
return null;
}
})
.orElse(TypeDescription.ForLoadedType.of(int.class));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAst;
import com.caoccao.javet.utils.SimpleFreeMarkerFormat;
import com.caoccao.javet.utils.SimpleMap;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.jar.asm.MethodVisitor;
Expand All @@ -40,6 +41,8 @@ default Size apply(MethodVisitor methodVisitor, Implementation.Context context)

Memo getMemo();

TypeDescription getType();

@Override
default boolean isValid() {
return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2024. caoccao.com Sam Cao
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.caoccao.javet.buddy.ts2java.ast.interfaces;

import com.caoccao.javet.buddy.ts2java.ast.memo.Ts2JavaMemo;
import com.caoccao.javet.swc4j.ast.interfaces.ISwc4jAstJsxAttrValue;

public interface ITs2JavaAstJsxAttrValue<AST extends ISwc4jAstJsxAttrValue, Memo extends Ts2JavaMemo>
extends ITs2JavaAst<AST, Memo> {
}
Loading

0 comments on commit 83946e8

Please sign in to comment.