mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-18 17:31:29 +00:00
Initial implementation of 'new'
This doesn't yet call the constructor or use provided parameters and just allocates raw memory
This commit is contained in:
22
src/ast.ts
22
src/ast.ts
@ -2,7 +2,7 @@ import {
|
||||
PATH_DELIMITER,
|
||||
STATIC_DELIMITER,
|
||||
INSTANCE_DELIMITER
|
||||
} from "./constants";
|
||||
} from "./program";
|
||||
|
||||
import {
|
||||
Token,
|
||||
@ -1508,16 +1508,6 @@ export function hasDecorator(name: string, decorators: Decorator[] | null): bool
|
||||
return getFirstDecorator(name, decorators) != null;
|
||||
}
|
||||
|
||||
/** Mangles a path to an internal path. */
|
||||
export function mangleInternalPath(path: string): string {
|
||||
// not necessary with current config
|
||||
// if (PATH_DELIMITER.charCodeAt(0) != CharCode.SLASH)
|
||||
// path = path.replace("/", PATH_DELIMITER);
|
||||
// if (PARENT_SUBST != "..")
|
||||
// path = path.replace("..", PARENT_SUBST);
|
||||
return path;
|
||||
}
|
||||
|
||||
/** Mangles a declaration's name to an internal name. */
|
||||
export function mangleInternalName(declaration: DeclarationStatement, asGlobal: bool = false): string {
|
||||
var name = declaration.name.name;
|
||||
@ -1535,3 +1525,13 @@ export function mangleInternalName(declaration: DeclarationStatement, asGlobal:
|
||||
return name;
|
||||
return declaration.range.source.internalPath + PATH_DELIMITER + name;
|
||||
}
|
||||
|
||||
/** Mangles an external to an internal path. */
|
||||
export function mangleInternalPath(path: string): string {
|
||||
// not necessary with current config
|
||||
// if (PATH_DELIMITER.charCodeAt(0) != CharCode.SLASH)
|
||||
// path = path.replace("/", PATH_DELIMITER);
|
||||
// if (PARENT_SUBST != "..")
|
||||
// path = path.replace("..", PARENT_SUBST);
|
||||
return path;
|
||||
}
|
||||
|
@ -37,7 +37,8 @@ import {
|
||||
Global,
|
||||
FunctionPrototype,
|
||||
Local,
|
||||
ElementFlags
|
||||
ElementFlags,
|
||||
Class
|
||||
} from "./program";
|
||||
|
||||
/** Initializes the specified program with built-in constants and functions. */
|
||||
@ -1824,3 +1825,26 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
|
||||
compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range);
|
||||
return module.createUnreachable();
|
||||
}
|
||||
|
||||
/** Compiles an allocation of the specified class. */
|
||||
export function compileAllocate(compiler: Compiler, cls: Class, reportNode: Node): ExpressionRef {
|
||||
var program = cls.program;
|
||||
var prototype = program.elements.get(compiler.options.allocateImpl);
|
||||
if (prototype) {
|
||||
var instance = (<FunctionPrototype>prototype).resolve(); // reports
|
||||
if (instance) {
|
||||
var usizeType = program.target == Target.WASM64 ? Type.usize64 : Type.usize32;
|
||||
if (!instance.is(ElementFlags.GENERIC) && instance.returnType == usizeType && instance.parameters.length == 1 && instance.parameters[0].type == usizeType) {
|
||||
if (compiler.compileFunction(instance)) // reports
|
||||
return compiler.makeCall(instance, [
|
||||
program.target == Target.WASM64
|
||||
? compiler.module.createI64(cls.currentMemoryOffset)
|
||||
: compiler.module.createI32(cls.currentMemoryOffset)
|
||||
]);
|
||||
} else
|
||||
program.error(DiagnosticCode.Implementation_0_must_match_the_signature_1, reportNode.range, compiler.options.allocateImpl, "(size: usize): usize");
|
||||
}
|
||||
} else
|
||||
program.error(DiagnosticCode.Cannot_find_name_0, reportNode.range, compiler.options.allocateImpl);
|
||||
return compiler.module.createUnreachable();
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
import {
|
||||
compileCall as compileBuiltinCall,
|
||||
compileGetConstant as compileBuiltinGetConstant
|
||||
compileGetConstant as compileBuiltinGetConstant,
|
||||
compileAllocate as compileBuiltinAllocate
|
||||
} from "./builtins";
|
||||
|
||||
import {
|
||||
PATH_DELIMITER
|
||||
} from "./constants";
|
||||
|
||||
import {
|
||||
DiagnosticCode,
|
||||
DiagnosticEmitter
|
||||
@ -45,7 +42,8 @@ import {
|
||||
VariableLikeElement,
|
||||
Flow,
|
||||
FlowFlags,
|
||||
ElementFlags
|
||||
ElementFlags,
|
||||
PATH_DELIMITER
|
||||
} from "./program";
|
||||
|
||||
import {
|
||||
@ -146,6 +144,10 @@ export class Options {
|
||||
noAssert: bool = false;
|
||||
/** If true, does not set up a memory. */
|
||||
noMemory: bool = false;
|
||||
/** Memory allocation implementation to use. */
|
||||
allocateImpl: string = "allocate_memory";
|
||||
/** Memory freeing implementation to use. */
|
||||
freeImpl: string = "free_memory";
|
||||
}
|
||||
|
||||
/** Indicates the desired kind of a conversion. */
|
||||
@ -545,15 +547,15 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return true;
|
||||
|
||||
var declaration = instance.prototype.declaration;
|
||||
if (!declaration)
|
||||
throw new Error("declaration expected"); // built-ins are not compiled here
|
||||
|
||||
if (instance.is(ElementFlags.DECLARED)) {
|
||||
if (declaration.statements) {
|
||||
if (declaration && declaration.statements) {
|
||||
this.error(DiagnosticCode.An_implementation_cannot_be_declared_in_ambient_contexts, declaration.name.range);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!declaration)
|
||||
throw new Error("declaration expected"); // built-ins are not compiled here
|
||||
if (!declaration.statements) {
|
||||
this.error(DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration, declaration.name.range);
|
||||
return false;
|
||||
@ -566,6 +568,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// compile statements
|
||||
var stmts: ExpressionRef[] | null = null;
|
||||
if (!instance.is(ElementFlags.DECLARED)) {
|
||||
declaration = assert(declaration);
|
||||
var previousFunction = this.currentFunction;
|
||||
this.currentFunction = instance;
|
||||
var statements = assert(declaration.statements);
|
||||
@ -601,12 +604,12 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
// create the function
|
||||
if (instance.is(ElementFlags.DECLARED)) {
|
||||
this.module.addFunctionImport(instance.internalName, instance.prototype.namespace ? instance.prototype.namespace.simpleName : "env", declaration.name.name, typeRef);
|
||||
this.module.addFunctionImport(instance.internalName, instance.prototype.namespace ? instance.prototype.namespace.simpleName : "env", instance.simpleName, typeRef);
|
||||
} else {
|
||||
this.module.addFunction(instance.internalName, typeRef, typesToNativeTypes(instance.additionalLocals), this.module.createBlock(null, <ExpressionRef[]>stmts, NativeType.None));
|
||||
}
|
||||
instance.finalize();
|
||||
if (declaration.range.source.isEntry && declaration.isTopLevelExport) {
|
||||
if (declaration && declaration.range.source.isEntry && declaration.isTopLevelExport) {
|
||||
this.module.addFunctionExport(instance.internalName, declaration.name.name);
|
||||
}
|
||||
return true;
|
||||
@ -2637,7 +2640,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return this.makeCall(functionInstance, operands);
|
||||
}
|
||||
|
||||
private makeCall(functionInstance: Function, operands: ExpressionRef[] | null = null): ExpressionRef {
|
||||
/** Makes a call operation as is. */
|
||||
makeCall(functionInstance: Function, operands: ExpressionRef[] | null = null): ExpressionRef {
|
||||
if (!(functionInstance.is(ElementFlags.COMPILED) || this.compileFunction(functionInstance)))
|
||||
return this.module.createUnreachable();
|
||||
|
||||
@ -2791,7 +2795,20 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
compileNewExpression(expression: NewExpression, contextualType: Type): ExpressionRef {
|
||||
throw new Error("not implemented");
|
||||
var resolved = this.program.resolveExpression(expression.expression, this.currentFunction); // reports
|
||||
if (resolved) {
|
||||
if (resolved.element.kind == ElementKind.CLASS_PROTOTYPE) {
|
||||
var prototype = <ClassPrototype>resolved.element;
|
||||
var instance = prototype.resolveInclTypeArguments(expression.typeArguments, null, expression); // reports
|
||||
if (instance) {
|
||||
// TODO: call constructor
|
||||
this.currentType = instance.type;
|
||||
return compileBuiltinAllocate(this, instance, expression);
|
||||
}
|
||||
} else
|
||||
this.error(DiagnosticCode.Cannot_use_new_with_an_expression_whose_type_lacks_a_construct_signature, expression.expression.range);
|
||||
}
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
|
||||
compileParenthesizedExpression(expression: ParenthesizedExpression, contextualType: Type): ExpressionRef {
|
||||
|
@ -1,14 +0,0 @@
|
||||
// internal naming scheme
|
||||
|
||||
/** Path delimited inserted between file system levels. */
|
||||
export const PATH_DELIMITER = "/";
|
||||
/** Substitution used to indicate the parent directory. */
|
||||
export const PARENT_SUBST = "..";
|
||||
/** Function name prefix used for getters. */
|
||||
export const GETTER_PREFIX = "get:";
|
||||
/** Function name prefix used for setters. */
|
||||
export const SETTER_PREFIX = "set:";
|
||||
/** Delimiter used between class names and instance members. */
|
||||
export const INSTANCE_DELIMITER = "#";
|
||||
/** Delimiter used between class and namespace names and static members. */
|
||||
export const STATIC_DELIMITER = ".";
|
@ -12,6 +12,7 @@ export enum DiagnosticCode {
|
||||
Structs_cannot_implement_interfaces = 108,
|
||||
Invalid_regular_expression_flags = 109,
|
||||
Type_0_cannot_be_reinterpreted_as_type_1 = 110,
|
||||
Implementation_0_must_match_the_signature_1 = 111,
|
||||
Unterminated_string_literal = 1002,
|
||||
Identifier_expected = 1003,
|
||||
_0_expected = 1005,
|
||||
@ -70,6 +71,7 @@ export enum DiagnosticCode {
|
||||
_super_can_only_be_referenced_in_a_derived_class = 2335,
|
||||
Property_0_does_not_exist_on_type_1 = 2339,
|
||||
Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures = 2349,
|
||||
Cannot_use_new_with_an_expression_whose_type_lacks_a_construct_signature = 2351,
|
||||
A_function_whose_declared_type_is_not_void_must_return_a_value = 2355,
|
||||
The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access = 2357,
|
||||
The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access = 2364,
|
||||
@ -101,6 +103,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 108: return "Structs cannot implement interfaces.";
|
||||
case 109: return "Invalid regular expression flags.";
|
||||
case 110: return "Type '{0}' cannot be reinterpreted as type '{1}'.";
|
||||
case 111: return "Implementation '{0}' must match the signature '{1}'.";
|
||||
case 1002: return "Unterminated string literal.";
|
||||
case 1003: return "Identifier expected.";
|
||||
case 1005: return "'{0}' expected.";
|
||||
@ -159,6 +162,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 2335: return "'super' can only be referenced in a derived class.";
|
||||
case 2339: return "Property '{0}' does not exist on type '{1}'.";
|
||||
case 2349: return "Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.";
|
||||
case 2351: return "Cannot use 'new' with an expression whose type lacks a construct signature.";
|
||||
case 2355: return "A function whose declared type is not 'void' must return a value.";
|
||||
case 2357: return "The operand of an increment or decrement operator must be a variable or a property access.";
|
||||
case 2364: return "The left-hand side of an assignment expression must be a variable or a property access.";
|
||||
|
@ -10,6 +10,7 @@
|
||||
"Structs cannot implement interfaces.": 108,
|
||||
"Invalid regular expression flags.": 109,
|
||||
"Type '{0}' cannot be reinterpreted as type '{1}'.": 110,
|
||||
"Implementation '{0}' must match the signature '{1}'.": 111,
|
||||
|
||||
"Unterminated string literal.": 1002,
|
||||
"Identifier expected.": 1003,
|
||||
@ -70,6 +71,7 @@
|
||||
"'super' can only be referenced in a derived class.": 2335,
|
||||
"Property '{0}' does not exist on type '{1}'.": 2339,
|
||||
"Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.": 2349,
|
||||
"Cannot use 'new' with an expression whose type lacks a construct signature.": 2351,
|
||||
"A function whose declared type is not 'void' must return a value.": 2355,
|
||||
"The operand of an increment or decrement operator must be a variable or a property access.": 2357,
|
||||
"The left-hand side of an assignment expression must be a variable or a property access.": 2364,
|
||||
|
@ -6,14 +6,6 @@ import {
|
||||
Target
|
||||
} from "./compiler";
|
||||
|
||||
import {
|
||||
PATH_DELIMITER,
|
||||
GETTER_PREFIX,
|
||||
SETTER_PREFIX,
|
||||
STATIC_DELIMITER,
|
||||
INSTANCE_DELIMITER
|
||||
} from "./constants";
|
||||
|
||||
import {
|
||||
DiagnosticCode,
|
||||
DiagnosticMessage,
|
||||
@ -81,6 +73,19 @@ import {
|
||||
NativeType
|
||||
} from "./module";
|
||||
|
||||
/** Path delimiter inserted between file system levels. */
|
||||
export const PATH_DELIMITER = "/";
|
||||
/** Substitution used to indicate the parent directory. */
|
||||
export const PARENT_SUBST = "..";
|
||||
/** Function name prefix used for getters. */
|
||||
export const GETTER_PREFIX = "get:";
|
||||
/** Function name prefix used for setters. */
|
||||
export const SETTER_PREFIX = "set:";
|
||||
/** Delimiter used between class names and instance members. */
|
||||
export const INSTANCE_DELIMITER = "#";
|
||||
/** Delimiter used between class and namespace names and static members. */
|
||||
export const STATIC_DELIMITER = ".";
|
||||
|
||||
class QueuedExport {
|
||||
isReExport: bool;
|
||||
referencedName: string;
|
||||
|
@ -84,7 +84,14 @@ glob.sync(filter, { cwd: __dirname + "/compiler" }).forEach(filename => {
|
||||
externalFunc: function(arg0, arg1, arg2) {
|
||||
console.log("env.externalFunc called with: " + arg0 + ", " + arg1 + ", " + arg2);
|
||||
},
|
||||
externalConst: 1
|
||||
externalConst: 1,
|
||||
allocate_memory: function(size) {
|
||||
console.log("env.allocate_memory called with: " + size);
|
||||
return 0;
|
||||
},
|
||||
free_memory: function(ptr) {
|
||||
console.log("env.free_memory called with: " + ptr);
|
||||
}
|
||||
},
|
||||
external: {
|
||||
externalFunc: function(arg0, arg1, arg2) {
|
||||
|
@ -24,6 +24,6 @@ function getZero(): i32 {
|
||||
}
|
||||
|
||||
export enum NonConstant {
|
||||
ZERO = getZero(),
|
||||
ONE
|
||||
ZERO = getZero(), // cannot export a mutable global
|
||||
ONE // cannot export a mutable global (tsc doesn't allow this)
|
||||
}
|
||||
|
20
tests/compiler/new.optimized.wast
Normal file
20
tests/compiler/new.optimized.wast
Normal file
@ -0,0 +1,20 @@
|
||||
(module
|
||||
(type $ii (func (param i32) (result i32)))
|
||||
(type $v (func))
|
||||
(import "env" "allocate_memory" (func $new/allocate_memory (param i32) (result i32)))
|
||||
(memory $0 1)
|
||||
(export "test" (func $new/test))
|
||||
(export "memory" (memory $0))
|
||||
(func $new/test (; 1 ;) (type $v)
|
||||
(drop
|
||||
(call $new/allocate_memory
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
(drop
|
||||
(call $new/allocate_memory
|
||||
(i32.const 8)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
15
tests/compiler/new.ts
Normal file
15
tests/compiler/new.ts
Normal file
@ -0,0 +1,15 @@
|
||||
class Simple {
|
||||
field: i32;
|
||||
}
|
||||
|
||||
class Generic<T> {
|
||||
field: T;
|
||||
}
|
||||
|
||||
@global
|
||||
declare function allocate_memory(size: usize): usize;
|
||||
|
||||
export function test(): void {
|
||||
var simple = new Simple();
|
||||
var generic = new Generic<f64>();
|
||||
}
|
76
tests/compiler/new.wast
Normal file
76
tests/compiler/new.wast
Normal file
@ -0,0 +1,76 @@
|
||||
(module
|
||||
(type $ii (func (param i32) (result i32)))
|
||||
(type $v (func))
|
||||
(import "env" "allocate_memory" (func $new/allocate_memory (param i32) (result i32)))
|
||||
(global $HEAP_BASE i32 (i32.const 4))
|
||||
(memory $0 1)
|
||||
(export "test" (func $new/test))
|
||||
(export "memory" (memory $0))
|
||||
(func $new/test (; 1 ;) (type $v)
|
||||
(local $0 i32)
|
||||
(local $1 i32)
|
||||
(set_local $0
|
||||
(call $new/allocate_memory
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
(set_local $1
|
||||
(call $new/allocate_memory
|
||||
(i32.const 8)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(;
|
||||
[program.elements]
|
||||
GLOBAL: NaN
|
||||
GLOBAL: Infinity
|
||||
FUNCTION_PROTOTYPE: isNaN
|
||||
FUNCTION_PROTOTYPE: isFinite
|
||||
FUNCTION_PROTOTYPE: clz
|
||||
FUNCTION_PROTOTYPE: ctz
|
||||
FUNCTION_PROTOTYPE: popcnt
|
||||
FUNCTION_PROTOTYPE: rotl
|
||||
FUNCTION_PROTOTYPE: rotr
|
||||
FUNCTION_PROTOTYPE: abs
|
||||
FUNCTION_PROTOTYPE: max
|
||||
FUNCTION_PROTOTYPE: min
|
||||
FUNCTION_PROTOTYPE: ceil
|
||||
FUNCTION_PROTOTYPE: floor
|
||||
FUNCTION_PROTOTYPE: copysign
|
||||
FUNCTION_PROTOTYPE: nearest
|
||||
FUNCTION_PROTOTYPE: reinterpret
|
||||
FUNCTION_PROTOTYPE: sqrt
|
||||
FUNCTION_PROTOTYPE: trunc
|
||||
FUNCTION_PROTOTYPE: load
|
||||
FUNCTION_PROTOTYPE: store
|
||||
FUNCTION_PROTOTYPE: sizeof
|
||||
FUNCTION_PROTOTYPE: select
|
||||
FUNCTION_PROTOTYPE: unreachable
|
||||
FUNCTION_PROTOTYPE: current_memory
|
||||
FUNCTION_PROTOTYPE: grow_memory
|
||||
FUNCTION_PROTOTYPE: changetype
|
||||
FUNCTION_PROTOTYPE: assert
|
||||
FUNCTION_PROTOTYPE: i8
|
||||
FUNCTION_PROTOTYPE: i16
|
||||
FUNCTION_PROTOTYPE: i32
|
||||
FUNCTION_PROTOTYPE: i64
|
||||
FUNCTION_PROTOTYPE: u8
|
||||
FUNCTION_PROTOTYPE: u16
|
||||
FUNCTION_PROTOTYPE: u32
|
||||
FUNCTION_PROTOTYPE: u64
|
||||
FUNCTION_PROTOTYPE: bool
|
||||
FUNCTION_PROTOTYPE: f32
|
||||
FUNCTION_PROTOTYPE: f64
|
||||
FUNCTION_PROTOTYPE: isize
|
||||
FUNCTION_PROTOTYPE: usize
|
||||
GLOBAL: HEAP_BASE
|
||||
CLASS_PROTOTYPE: new/Simple
|
||||
CLASS_PROTOTYPE: new/Generic
|
||||
FUNCTION_PROTOTYPE: new/allocate_memory
|
||||
FUNCTION_PROTOTYPE: allocate_memory
|
||||
FUNCTION_PROTOTYPE: new/test
|
||||
[program.exports]
|
||||
FUNCTION_PROTOTYPE: allocate_memory
|
||||
FUNCTION_PROTOTYPE: new/test
|
||||
;)
|
Reference in New Issue
Block a user